局部变量的可见性

1 问题的引出

局部变量跨线程,又不能用volatile,怎么保证其可见性

2 是否真的能有局部变量跨线程写入?

看一下这段代码:

    public static void main(String []f) {
        Integer integer = 1;
        new Thread(new InnerThread(integer)).start();
        while(true) {
            if(integer == 2) break;
        }
    }

    private static class InnerThread implements Runnable {
        private Integer innerInteger;

        public InnerThread(Integer integer) {
            this.innerInteger = integer;
        }

        @Override
        public void run() {
            innerInteger = 2;
        }
    }

  

 这个代码有没有可见性问题?答案是没有的

两条线程访问的其实是2个变量,是没有线程并发可见性问题的,因为java不像c++那样有变量的引用语法,&integer,子线程的构造其实是c++里面的值传递而不是引用传递,什么叫值传递?引用传递?

    public static void main(String []f) {
        Integer x = 1;
        setInt(x);
        System.out.println(x);

    }

    private static void setInt(Integer x) {
        x = 2;
    }

打印1,值传递,打印2,引用传递

  

子线程写入的是线程内部的局部对象,主线程读取的是main方法的局部对象,根本是2个变量,自然无可见性问题

3 那么有没有使用一个局部对象的多线程写入?答案是有的

    public static void main(String []f) {
        Integer integer = 1;
        new Thread(new Runnable() {
            @Override
            public void run() {
                integer = 2;
            }
        }).start();
        while(true) {
            if(integer == 2) break;
        }
    }

  

然而,这个代码是过不了编译的,必须在integer上加上final

    public static void main(String []f) {
        final Integer integer = 1;
        new Thread(new Runnable() {
            @Override
            public void run() {
                integer = 2;
            }
        }).start();
        while(true) {
            if(integer == 2) break;
        }
    }

  

然而加上final后,integer=2自然非法了

所以归根结底jdk从语法上避免了这个问题的发生

5 扩展思考

public class TestMain {

    private volatile TestC testC;

    private static class TestC {
        private Integer integer;

        public Integer getInteger() {
            return integer;
        }

        public void setInteger(Integer integer) {
            this.integer = integer;
        }
    }
}

  

这个代码能保证integer的可见性吗,答案是不能,volatile修饰的是testC,而不是integer,这个性质有点像Unsafe控制ConcurrentHashMap内并发数组元素的可见性   ,数组对象volatile,数组元素没有volatile,数组元素一样会有可见性问题

6

    public static void main(String []f) {
        TestC testC = new TestC();
        testC.setInteger(1);
        new Thread(new Runnable() {
            @Override
            public void run() {
                testC.setInteger(2);
            }
        }).start();
        while(true) {
            if(testC.getInteger() == 2) break;
        }
    }

    private static class TestC {
        private Integer integer;

        public Integer getInteger() {
            return integer;
        }

        public void setInteger(Integer integer) {
            this.integer = integer;
        }
    }

  

这个代码是否实现了第2点所说的“局部变量跨线程写入”?

对不起,这个不属于写入,这里的“写入”指的是,让共享变量作为=的左值,此之谓“写入”,比如:

    public static void main(String []f) {
        TestC testC = new TestC();
        testC.setInteger(1);
        new Thread(new Runnable() {
            @Override
            public void run() {
                TestC tmp = new TestC();
                tmp.setInteger(2);
                testC = tmp;
            }
        }).start();
        while(true) {
            if(testC.getInteger() == 2) break;
        }
    }

    private static class TestC {
        private Integer integer;

        public Integer getInteger() {
            return integer;
        }

        public void setInteger(Integer integer) {
            this.integer = integer;
        }
    }

  

此时又会报,要加final的提示

我们得到重要结论:局部变量永远不能被多线程写入,既然无法多线程写入,自然也没有可见性问题

原文地址:https://www.cnblogs.com/silyvin/p/14964075.html