线程--valatile和synchronized

问题

并发变成中的两个关键问题

线程之间如何通信

  1. 共享内存 – 隐式通信
  2. 消息传递 – 显式通信

线程之间如何同步

  • 在共享内存的并发模型中,同步是显示做的,比如:synchronized
  • 在消息传递的并发模型中,由于消息的发送必须在消息接收前,所以同步是隐式的

数据可见性

synchronized

当释放锁的时候回吧副本中数据同步到主内存中,
当线程获取到锁的时候回到主内存中同步数据到副本中

volatile

volatile可以做到原子性、可见性,但是不能保证做到复合操作的原子性。比如:i++、i–等等。

  1. 被volatile声明的变量进行写操作额时候,jvm会向处理器发送lock前缀的指令,将变量所在缓存行的数据写回到主内存中
  2. 在多处理器的情况下,保证各个处理器缓存一致性的特性,就会实现一致性协议

不支持复合操作的原子性

当执行x++,其实是执行了三步操作

  1. 从主内存读取值x=0;
  2. 执行x=0+1;
  3. 再把x=1刷回主内存;

当a,b同时操作x共享变量时,都执行到第一步,同时读取了x的值为0,此时a变量率先执行完到第2步第3步,x的值为1刷回到主内存了,但b线程已经执行到第2步了,即使x此时的值为1,b线程执行第2步的操作时是x=0+1.而不是x=x+1,所以最后结果还是1,这就是为什么复合操作不能保证原子性的原因。

synchronized



monitorenter和monitorexit来控制同步代码块,通过获取对象的监视器。
线程在获取锁的时候,其指针指向的是一个monitor对象(由C++实现)的起始地址。每个对象实例都会有一个 monitor。其中monitor可以与对象一起创建、销毁;亦或者当线程试图获取对象锁时自动生成。而monitor是添加Synchronized关键字之后独有的。synchronized同步块使用了monitorenter和monitorexit指令实现同步,这两个指令,本质上都是对一个对象的监视器(monitor)进行获取,这个过程是排他的,也就是说同一时刻只能有一个线程获取到由synchronized所保护对象的监视器。 线程执行到monitorenter指令时,会尝试获取对象所对应的monitor所有权,也就是尝试获取对象的锁,而执行monitorexit,就是释放monitor的所有权。