Volatile、Static、Synchronized的可见性

2018-06-25  本文已影响0人  冰鱼飞鸟

1.Static并不能保证在各个线程中值的一致,因为每个线程有自己的工作内存,工作内存中的变量值是主内存中的拷贝,没有重新从主内存中加载时,并不能知道别的线程对这个变量做了什么修改。

    public static boolean isRun = true;
    public static void main(String[] args) throws InterruptedException {
        Thread1 t1 = new Thread1(1);
        t1.start();
        
        Thread.sleep(1000);
        isRun = false;
        System.err.println("--------stop");;
    }
    
    static class Thread1 extends Thread{
        private int flag;
        public Thread1(int flag) {
            this.flag = flag;
        }
        
        @Override
        public void run() {
            super.run();
            while(true) {
                if(!isRun) {
                    System.err.println(flag+"," + isRun);
                    break;
                }
            }
        }
    }

结果如下

--------stop

结果是stop已经打印出来,线程中的循环结束不了,因为线程的工作内存中的isRun值还是true,并没有重新从主内存中获取修改后的值。

2.Volatile,java内存模型保证volatile修饰的变量在各个线程中保持一致,即可见性保证。当一个线程修改了该变量的值,修改后的值可以立即被其他线程获取到。
(ps:普通变量的值如果要在线程间传递要通过主内存做过渡,及A线程要把修改后的值同步回主内存,B线程要从主内存中获取才能获得普通变量的新值。)
(ps: Volatile保证可见性,并不能保证对volatile的所有操作都是线程安全,这还要考虑操作的原子性,如volatile int i; i++;并不是原子操作,靠volatile不能保证其线程安全。)

将上面例子中的isRun加上volatile修饰。

public static volatile boolean isRun = true;
//  public static boolean isRun = true;
    public static void main(String[] args) throws InterruptedException {
        Thread1 t1 = new Thread1(1);
        t1.start();
        
        Thread.sleep(1000);
        isRun = false;
        System.err.println("--------stop");;
    }
    
    static class Thread1 extends Thread{
        private int flag;
        public Thread1(int flag) {
            this.flag = flag;
        }
        
        @Override
        public void run() {
            super.run();
            while(true) {
                if(!isRun) {
                    System.err.println(flag+"," + isRun);
                    break;
                }
                
            }
        }
    }

结果如下

--------stop
1,false

3.Synchronized 在保证互斥的同时,还可以保证可见。在进入同步代码块时会使当前线程的工作内存失效,强制从主内存重新获取。
所以如第一个例子在线程中加上synchronized代码块时

public static boolean isRun = true;
    public static void main(String[] args) throws InterruptedException {
        Thread1 t1 = new Thread1(1);
        t1.start();
        
        Thread.sleep(1000);
        isRun = false;
        System.err.println("--------stop");;
    }
    
    static class Thread1 extends Thread{
        private int flag;
        public Thread1(int flag) {
            this.flag = flag;
        }
        
        @Override
        public void run() {
            super.run();
            while(true) {
                if(!isRun) {
                    System.err.println(flag+"," + isRun);
                    break;
                }
                //System.err.println("thread is running.");
                synchronized ("") {
                }
                
            }
        }
    }

结果如下

--------stop
1,false

另外一点在测试中发现的是,其实在线程中System.....print和加同步代码块是一样的效果。看System....print的源码,其实就是synchronized。

public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }
上一篇下一篇

猜你喜欢

热点阅读