共享模型之内存
可见性
由于编译器优化,jvm会对多次从内存中读到的数据加载到缓存中,下次读取会直接从缓存中读取
因此当main线程对内存中的数据改写后,thread线程依然读到的是缓存中的数据,导致没办法退出循环。
static booleen run = true; Thread thread = new Thread(()->{ while(run){ if(run==false){ break; } } }); thread.start(); sleep(1); run = false;
可见性解决
可以使用synchronized来解决这个问题,synchronized中的赋值代码会同步到内存中。
也可以使用volatile解决,用volatile修饰的变量修改后会同步内存中。读取也会从内存中读取
指令重排序
jvm会对jvm指令进行重排序来提高性能。指令重排序的原则是不会影响运行结果,但是当多线程情况下便会出现问题
解决可以用volatile解决
原理 volatile 修饰的变量,在读取操作前会加读屏障,使后面的代码对读取操作都是读取到的内存中的值。而在对volatile的变量进行写操作时,会增加写屏障, 保证写屏障之前所有的写操作都会同步到内存中。
读屏障会保证读屏障后的代码不会重排序到读屏障前面
写屏障会保证写屏障前的代码不会重排序到写屏障后面
double-checked-locking (dcl )
private static object instance = null; private object getinstance (){ if(instance == null) { synchronized(object.class) { if(instance == null ) { instance = new object(); } } } return instance; }
上述代码是有问题的如果第一个线程执行到instance = new object()这一步,在jvm指令上有两步,先调用object的构造方法,在赋值给instance 但是如果发生指令重排序 问题,就会导致先赋值后调用构造方法,如果赋值后还没调用构造方法,第二个线程就进入了外层的判断语句,发现instance!=null 这个时候会返回一个空的instance对象,
所以解决方法就是在instance变量上加volatile 让赋值语句和构造方法语句不发生重排序
private static volatile object instance= null;
在synchronized中增加第二次判断是因为,如果两个线程都在赋值前进入了第一个if判断,这时一个阻塞住,一个进入创建对象,如果没加第二个if 就会导致第二个线程进入synchrionized后也创建一个对象,创建了两个对象。