synchronized和volatile
一,两种线程安全问题
1,执行顺序导致的线程安全问题
当多个线程同时访问一些数据的时候,很容易导致数据的状态出现随机的不一致性,这就是典型的线程安全问题。
//假定long count是个全局变量,初始值为10
| 线程1 | 线程2 |
| sum1 = count + 100; count = sum1; | sum2 = count + 10; count = sum2; |
下面给出其中两种执行顺序(线程2的代码靠右):
| 第一种执行顺序 | 第二种执行顺序 | ||||||||||||||||||||
|
|
2,cpu cache导致的线程安全问题
为了高效,cpu对于全局变量的访问不一定都从内存中读取,而是为不同线程缓存一个副本在cpu的高速cache中,这样线程中访问的全局变量的值未必是最新更新的值,可能仅仅是cpu对该线程某个时刻对该变量值的缓存。
同样拿上面的例子,如果考虑cpu cache的话,即使第一种执行顺序,count的结果也同样可能是20!执行顺序和解释如下:
| 第一种执行顺序 | ||||||||||||
|
二,synchronized基本概念
synchronized蕴含着对某个对象monitor获取的的操作,如果对应对象monitor被其他线程所拥有,则synchronized对应的代码会堵塞,直到对应对象monitor被其拥有者线程释放。
这样能保证synchronized是串行(顺序执行的),下面给出了使用synchronized解决1.2.1中出现的问题的示例:
| 线程1 | 线程2 |
| synchronized (a) { sum1 = count + 100; count = sum1; } | synchronized (a) { sum2 = count + 10; count = sum2; } |
*注意:synchronized 中的代码段在执行前会强制cpu重新从内存中读取全局变量的值,所以不存在上文“2,cpu cache导致的线程安全问题”中提到的问题。
三,volatile基本概念
带volatile修饰符声明的变量,在多线程并发访问的时候,会强制cpu重新从内存中读取变量的值,也避免了上文“2,cpu cache导致的线程安全问题”中提到的问题。
| volatile long count = 10; |
四,简单示例
成员方法上的synchronized修饰符
| public synchronized void myfun() { //do something } |
等价于
| public void myfun() { synchronized(this){ //do something } } |
静态方法上的synchronized修饰符
| public class MyClass { public synchronized static void myStaticFun() { //do something } } |
等价于
| public class MyClass { |




