个人理解仅供参考。
synchronized对于同一颗cpu或者说同一个核来说,是原子性的。诸多线程只能同步访问被同步的域。这些线程之间必须形成竞争关系,竞争被同步的资源。
实例:
static int a;
synchronized(a){
//do something here
...
}
a就是被抢占的资源,也正是因为这个破a,诸多线程之间才形成了竞争关系。
听起来好像a一定不会出错了,因为所有的线程都是对a顺序操作了。
但很不幸忽略了一个问题,对a上的锁是不牢靠的,不在于synchronized本身,而是a本身。
假如你有两个cpu,每一个cpu都有独立的工作内存(寄存器或者缓存之类的什么玩意),每个cpu都跑着5个线程,总共十个,他们都抢占a,问题来了:
A CPU和B CPU都想操作 a, 但他们两个CPU上的线程不具备竞争条件,他们有各自的时钟,和各自的工作内存。
假设此时a == 0;
A CPU操作a从主存读到A CPU内存,此时B也要多a操作,他也把a读到自己的CPU内存中。
a此时在俩个CPU内存中都是0。
A CPU上的线程A1抢占到了时间片,A2,A3,A4,A5都等待,A1把a加1,然后写入主存。
现在内存里a的值是1了。
在我们描述商议过程的时候B CPU里也发生了同样的事情,线程B1抢占了B CPU里的a(此时a还是0呢),然后把a加1,写会主存。
干了a的值被加了两次,可惜他还是1。
此时volatile关键字就解决了这个问题,他的出现告诉cpu别从你得存储里拿a,从主存里拿! 这样就避免了CPU各自处理各自的a的副本的情况。
但很遗憾,volitle基本上不是原子的。他没办法解决多个线程并发访问带来的脏数据问题。
所以我们可以synchronized和volatile同时使用,把锁加在主存上。
总结:
synchronized同步CPU内存上的变量,但无法维护各CPU内存上的变量的值保持同步。
volatile可以保证所有线程访问的是同一个变量(在主存中,而没有副本),但他没办法保持线程同步。