java多线程开发中,控制共享数据比较麻烦,有可见性和同步性。通常控制可见性咱们能够经过synchronized和volatile控制,而同步性咱们只能经过synchronized或Lock来控制。java
我喜欢经过对一个问题的理解,来理解某个知识点,由于我以为知识就是为了解决问题。多线程
public class SynchronizedTest { private static boolean flag = true; public static void main(String[] args) throws Exception{ new Thread(new Runnable() { @Override public void run() { while(flag) { } System.out.println("子线程执行结束================"); } }).start(); Thread.sleep(1000); flag = false; // 关闭线程输出 System.out.println("flag已被修改成false"); } }
上面的代码在64位JVM机器(准确来讲应该是jvm的server模式)上执行通常都不会输出“子线程执行结束================”,而在32位的jvm的client模式下却能够输出“子线程执行结束================”。jvm
经过 java -version 能够查看本身本机jvm运行的模式。ide
注意:若是你的jvm装的是64位,那么是没法切换到client模式的。优化
上面的问题引发的缘由其实就是因为java的内存模型引发的,java内存模型中分有主内存和工做内存之分,主内存能够理解为共享数据的区域(不知道准确不许确),而工做内存(主内存数据的副本)是每一个线程私有的一块区域,每一个线程对共享数据的修改,不会直接操做共享数据,通常是先修改工做内存中的数据,而后在某个特定的时候刷新到主存,其余线程才有机会看到其修改。this
解决以上问题线程
private static volatile boolean flag = true;code
缘由:能够这么简单理解,加了volatile关键字的共享变量,全部线程对其值的获取或修改都直接经过主存,绕过来工做内存,因此主线程对flag的修改子线程立刻就能够看到。server
volatile的原理就是加内存屏障,全部的读都在写以后,这样保证了子线程对flag的访问在主线程对flag的修改以后。对象
new Thread(new Runnable() { @Override public void run() { int count = 0; while(flag) { synchronized(SynchronizedTest.class){ //这里能够锁任何共享对象 count++; } } System.out.println("子线程执行结束================"); } }).start();
上面之因此加上 count ,是防止JIT优化将无用的锁代码块优化掉。
缘由:因为jvm规定在进入synchronized以前会将全部其余线程的工做内存刷新到主存(这个我不太肯定是否正确,若是有肯定的请留言告诉我,谢谢),在离开synchronized块以前会将本线程的工做内存刷新到主内存。
在验证这个之中我踩过一个坑,个人代码以下:
new Thread(new Runnable() { @Override public void run() { while(flag) { System.out.println("====falg====="); } System.out.println("子线程执行结束================"); } }).start();
这样在任何状况下均可以输出"子线程执行结束================",找了好久才发现原来 System.out.println 方法中有synchronized同步块致使。
这个方法来自 System.out
public void println(String x) { synchronized (this) { print(x); newLine(); } }
之中有很多观点是我本身的观点,可能不许确,或是错误的,但愿大家能给我指出,谢谢。