背景:
今天同事在看我写锁同步的总结,兴致来了想考考我,问我synchronized
保证变量的可见性吗?
我回答可以,他的答案是synchronized不保证可见性,volatile才保证可见性。结果两人起来争执,为此他特意写了如下代码来证明他说的是正确的:
重点戏
/**
* 证明,synchronized能不能保重对象可见性。
*/
public class SynchronizedLock {
private static int count = 0;
public void incr() {
synchronized (this) {
count ++;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedLock synchronizedLock = new SynchronizedLock();
for (int i = 0; i < 1000; i++) {
new Thread(synchronizedLock::incr).start();
}
Thread.sleep(1000);
System.out.println(count);
}
}
他执行了下,他说明了之前看视频老师就是用这个实例,为什么有的时候,输出是:
999
有的时候是:
998
有的时候是:
1000
看到上面的代码,一开始我还真疑惑了一下。结果发现一个Thread.sleep(1000)
,这是个什么鬼,确定是最后两个线程/一个线程在println的时候已经执行了?其实确定不了,我让他把 Thread.sleep(1000);
改成 Thread.sleep(3000)
,看看打印的有998/999过吗?结果肯定是没出现过998/999。
当时就怼他的JMM基础不扎实,就只是会被网上的答案。
结论
JMM需要解决的问题就是Java多线程通信的问题,多线程通信的问题主要是三个问题:有序性、可见性和原子性。synchronized属于锁,锁也是需要解决通信的三个问题的。其中可见性是有锁的内存原语保证的:
1)获取锁的内存语义:当线程获取锁的时候,JMM会把线程对于的本地内存置为无效,从而使得被监视器保护的临界区代码必须从主内存读取共享变量。
2)释放锁的内存语义:当线程释放锁的时候,JMM会把该线程对应的本地内存中的共享变量刷新到内存中。
希望那个同事能看到这篇文章吧,他到现在都不理我了。