一 . JVM内存结构
在上图之中,我们可以注意到JVM为了加快运行,使用了缓存机制,但是在使用了缓存机制之后,就会出现缓存的不一致性的问题.
下面演示一个代码:
public class VolatileTest { private Boolean flag = true; public static void main(String[] args) { VolatileTest demo = new VolatileTest(); new Thread(() -> { for (;;) { if (!demo.flag) { System.out.println("end.."); return; } } }).start(); new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } demo.flag = false; }).start(); } }
上面的代码之中,我们期望在运行之后,我们设置的符号标记被修改掉,通过这样的方式,我们就能完成对第一个线程的停止.
但是结果出乎我们的意料,第一个线程永远不会终结.
二 .可见性
造成层上面的问题的根源就是JVM的缓存机制导致的,第一个线程使用的是一个线程栈之中的副本,而这个副本永远都是true,因此我们上面的方式时永远不能停止线程的.
可见性问题:
在多线程并发的情况下,由于JVM的缓存机制造成缓存之间的不同步,(一个线程对一个变量进行了修改,但是另外的线程无法及时的知晓),这就是可见性问题.
三 . 可见性问题的解决
既然可见性的问题就是缓存不一致性引发出来的,因此我们只需要一个缓存同步的方法就能解决这个问题.
在java之中,出现了volatile关键词,这个关键词帮助实现了对被volatile关键词修饰的变量一旦进行了修改,那么其他的线程一定会得到通知,从主存之中获取新的数据.
通过这种方式,就解决了可见性问题.
四 .轻量级同步方式
通过上面的介绍,我们可以看出volatile是一个轻量级的同步工具,帮助我们解决了线程安全性问题.
也就是说,可见性问题也是一种引起线程安全性问题的原因.
在这里我们可以看到,解决线程安全性问题的方式:
[1]原子性:
[2]可见性:
[3]有序性: 使用volatile也能解决一定的有序性.