问题
测试线程安全问题,下面代码,main线程会一直死循环,不会进入判断break。这是为什么呢?
/**
* Java内存模型,理解volatile解决,线程工作内存不一致问题
* 工作内存:线程的working memory是cpu的寄存器和高速缓存的抽象描述
* volatile: 保证每次使用变量,都是先从主内存上刷新到本地内存。所以,保证了可见性。
*/
public class JMM {
public static void main(String[] args) throws InterruptedException {
final Trigger trigger = new Trigger();
trigger.start();
for (;;) {
//System.out.print("."); //此处输出代码,会导致trigger能读到 start = true, 这是为什么呢?
/**
* 上述问题解释:线程的working memory只是cpu的寄存器和高速缓存的抽象描述。如果死循环读取,就会一致读取到高速缓存区的数据。
* 而缓存的数据就是start = false, 导致此循环一致无法结束。除非 Trigger 类的属性start用volatile修饰。
* 问题来了,那么什么情况下,工作内存才会被刷新呢?
*
*/
if (trigger.isStart()) {
System.out.println(Thread.currentThread().getName() + " trigger is start:启动成功");
break;
}
}
}
}
/**
* 思考:
* 1、volatile 保证了可见、有序,但是不能保证原子。所以还是会有线程安全问题,如:i++
* 2、但是操作boolean,以及赋值操作,天然是原子操作。结合上volatile,就没有了线程安全问题
*/
class Trigger extends Thread {
private boolean start = false;
//private volatile boolean start = false;
public boolean isStart() {
return start;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " get trigger start:" + start);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + " trigger set start = true");
start = true;
}
}
原因
了解java内存模型,每个线程都有自己的工作内存,工作内存都会从主内存拷贝一份变量(本质上就是高速缓存,这样设计就是为了更好的运行速度),上述循环一致读的是工作内存中的变量。这就是可见性问题,voliatile就是解决此问题的。
原理
volatile: 保证每次使用变量,都是先从主内存上刷新到本地内存。所以,保证了可见性。
线程安全
1. volatile 保证了可见、有序,但是不能保证原子。所以还是会有线程安全问题,如:i++
2. 但是操作boolean,以及赋值操作,天然是原子操作。结合上volatile,就没有了线程安全问题