11.一个诡异的可见性问题

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了11.一个诡异的可见性问题相关的知识,希望对你有一定的参考价值。

我们说synchronized能解决的三个问题是:原子性、可见性和有序性。那是否所有场景都需要同时解决这三个问题呢?不一定!看个例子:

public class VolatileExample 
    public  static boolean stop = false;
    public static void main(String[] args) throws InterruptedException 
        Thread t1 = new Thread(() -> 
            int i = 0;
            while (!stop) 
                i++;
            
        );
        t1.start();
        System.out.println("begin start thread");
        Thread.sleep(1000);
        stop = true;
    

这段代码的逻辑很简单,首先t1线程通过stop变量来判断是否结束循环,然后在main线程中通过修改stop变量的值来破坏t1线程的循环条件从而退出循环。但是实际情况是t1并没有按照期望输出。

注意如果你的JDK是client版本可能看不到效果。

我们while这里可以给i++加锁,也就是将上面t1的代码改成如下的样子,再运行就会让程序停下来

Thread t1 = new Thread(() -> 
    int i = 0;
    while (!stop) 
        synchronized (VolatileExample.class)
            i++;
        

    
);

或者我们添加一个打印和或者Thread.sleep(0),也就是将上面t1的代码改成如下的样子,再运行也会让程序停下来。

Thread t1 = new Thread(() -> 
    int i = 0;
    while (!stop) 
        System.out.println("thread t1");
        i++;
    
);

还有一个更牛的方式,增加一个创建文件的操作,此时也会让程序停下来:

Thread t1 = new Thread(() -> 
    int i = 0;
    while (!stop) 
        new File("");
        i++;
    
);

这是什么道理呢?很明显此时必然与new File()或者加锁等的底层机制有关系。

我们先看一下println的实现:

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

可以看到这里的println里加锁了,而且是类锁System。这里加了synchronized这个同步关键字,会防止循环期间对于stop值的缓存。因为println有加锁的操作,所以当其完成任务要释放锁的时候,会强制性的把工作内存中涉及到的写操作同步到主内存。 从IO角度来说,print本质上是一个IO的操作,我们知道磁盘IO的效率一定要比CPU的计算效率慢得多,所以IO可以使得CPU有时间去做内存刷新的事情,从而导致这个现象。比如我们可以在里面定义一个new File()。同样会达到效果。 Thread.sleep(0)生效的原因是导致线程切换,线程切换会导致缓存失效从而读取到了新的值。

在单线程的环境下,如果向一个变量先写入一个值,然后在没有写干涉的情况下读取这个变量的值,那这个时候读取到的这个变量的值应该是之前写入的那个值。这本来是一个很正常的事情。但是在多线程环境下,读和写发生在不同的线程中的时候,可能会出现:读线程不能及时的读取到其他线程写入的最新的值,这就是可见性。

为什么多线程环境下会存在可见性问题呢?

这主要是指令执行过程中存在重排序导致的,Server版本的编译器是面向服务器的,会做大量的优化,例如勿用代码消除,循环展开、消除公共子表达式等等。而本节最开始的代码之所以不能停止,就是因为代码被编译器优化了,我们直接在stop的定义前添加volatile关键字即可:

public volatile static boolean stop = false;

重排序问题具体咋回事?是不是只有重排序会带来可见性问题?不是的。我们后面继续讨论。

以上是关于11.一个诡异的可见性问题的主要内容,如果未能解决你的问题,请参考以下文章

解密诡异并发问题的幕后黑手:可见性问题

诡异并发三大恶人可见性

诡异的并发之有序性

动态换出或切换控件的可见性

使用复选框控制 DataTables 列的可见性

使输入表单不可见