Volatile关键字补充

前言

之前转载的文章volatile关键字解析中详细分析了volatile相关的知识(写的非常非常好!),这里就两点详细说下。

Synchonized保证可见性(以println方法为例)

之前有一次和朋友聊天说起System.out的各种print方法,当时他说这些方法内部除了IO外还有些别的逻辑,但当时没有特别在意。看了上面的博客后才意识到println通过同步的方法保证了可见性。用代码来解释吧:

public class AtomicIntegerTest {

    static volatile AtomicInteger x = new AtomicInteger(0); //1
    //static AtomicInteger x = new AtomicInteger(0);				//2

    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new TestThread1());
        service.execute(new TestThread2());
        service.shutdown();
    }

    static class TestThread1 implements Runnable{

        @Override
        public void run() {
            for(int i = 0;i < 100;i++){
                int y = x.incrementAndGet(); //3-1
                System.out.println("thread1 is " + "time is " + y + "result is " + x); //4-1
            }
        }
    }

    static class TestThread2 implements Runnable{

        @Override
        public void run() {
            for(int i = 0;i < 100;i++){
                int z = x.incrementAndGet(); //3-2
                System.out.println("thread2 is " + "time is " + z + "result is " + x); //4-2
            }
        }
    }

}

上面的code中,如果按照1处,我们对x加volatile关键字,那么在上面的程序中TestThread1和TestThread2开始运行后,因为y,z是局部变量,而x是共享变量,且3-1/4-1, 3-2/4-2这两步不是原子的,那么在运行过程中,因为volatile本身会保证可见性,所以很有可能有如下的情况:

  1. TestThread1执行了3-1,假设x从1->2,则x,y都为2;
  2. 此时切换到TestThread2,并执行了3-2, 4-2两步,则x, z都为3;
  3. 然后切换回TestThread1,因为x有volatile,所以此时TestThread1的工作内存内的x失效,TestThread1会从主存中重新读取x,那么会出现x,y不一致的情况,即x=3, y=2。

可以看到,图中蓝色部分的输出行确实出现了这种情况,说明了volatile的可见性。

然后我们按照代码中2中来试验,即删除x的volatile关键字,那么在上面第3步时,如果没有保证可见性,那么TestThread1便不会从主存中读取x,那么x和y的值会一直一样。我们可以实验一次,结果如下:

可以看到,就算不加volatile关键字,这里的输出一样会保证可见性,而进入println方法可以看到:

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

因为println方法内部有synchonized关键字,也验证了synchonized方法会保证可见性。

volatile的使用条件

  • 对变量的写操作不依赖于当前值。
  • 该变量没有包含在具有其他变量的不变式中。

其实第一点很好理解,如果写操作依赖当前值,那么就造成了原子性的缺失。之前一直不太理解第二点,后来参考了一些资料发现主要指的如果几个变量间存在默认的不变形关系,那么不能通过将这几个变量设置成为volatile的方法来保证正确性,因为volatile变量只能保证自己的可见性,没有办法去维护和别的volatile变量的关系。

可以看http://www.blogjava.net/bolo/archive/2014/06/20/414971.html的分析

参考

http://www.blogjava.net/bolo/archive/2014/06/20/414971.html

https://www.jianshu.com/p/cd4744d799e4
<https://blog.csdn.net/justloveyou_/article/details/53672005

posted @ 2019-04-25 21:58  SmallMushroom  阅读(169)  评论(0编辑  收藏  举报