先说一下我在学习Java多线程编程核心技术这本书时,遇到的一个问题。在学习volatile关键字时,下面代码是书上为演示死循环
的一个demo。事实上,它也确实一定会产生死循环(为什么死循环可以参考下方链接)。
但是我在测试时在for循环中加入了一个打印语句,仅仅这一行就打破了这个死循环,让我纠结了好久,为什么它不会产生死循环
了。我尝试把这行代码换成其他无关紧要的语句,比如 int i = 5;i++;Object obj = new Object();等等,它又会如预期中一样造成死
循环。于是我看了下打印语句的源码
所以我思考是不是加锁引起的,联想到死循环的原因,在当前环境下,this指的就是RunThread的实例对象thread,线程在获取锁
对象的时候肯定不是从线程私有堆栈中去获取吧,所以此时它会去公共堆栈中寻找thread,于是也就更新了私有堆栈中 thread,
所有死循环打破。为了验证猜想我在for循环中加入了空的同步代码块 synchronized (new Object()) {},结果如预期,依然是死循
环。锁对象换成this时,死循环打破。猜想似乎是正确的。其他操作,比如Thread.sleep(1000)也会打破死循环,这个原因我就不
太清楚了。
后来,在进一步学习过程中了解到,synchronized是具有volatile同步功能的,它有两个特征:1. 互斥性,同一时刻,只有一个线
程持有某个特定的锁;2.可见性,确保其他线程对共享数据做出的更改对于随后获得该锁的另一个线程是可见的(在获得锁对象
之前,会确认其他线程对其的使用已结束,并将修改写回公共内存中),从而保证一致性。
一个同步块可以保证在同一时刻仅有一个线程可以进入代码的临界区。同步块还可以保证代码块中所有被访问的变量将会从主存中
读入,当线程退出同步代码块时,所有被更新的变量都会被刷新回主存中去,不管这个变量是否被声明为volatile
言归正传,关于volatile关键字的学习我实在没什么研究,这里记一下看到的两篇文章,很详细,方便以后回头查看。
https://www.cnblogs.com/kubidemanong/p/9505944.html
简单归纳:
volatile关键字修饰的变量被视为共享变量,具有线程间可见性,在多线程环境下,如果一个共享变量被一个线程修改了之后,
当其他线程要读取这个变量的时候,最终会去内存中读取,而不是从自己的工作空间中读取。详细过程如下:
线程中的处理器会一直在总线上嗅探其内部缓存中的内存地址在其他处理器的操作情况,一旦嗅探到某处处理器打算修改其内存地址中的值,
而该内存地址刚好也在自己的内部缓存中,那么处理器就会强制让自己对该缓存地址的无效(缓存一致性原理)。
所以当该处理器要访问该数据的时候,由于发现自己缓存的数据无效了,就会去主存中访问。
绝大都数情况下,volatile 关键字能保证线程安全,但由于java的运算操作并非是原子操作,某些情况下,它也无法完全保证绝对的线程安全。
例子分析见上面链接。