多线程之:java的CAS操作的相关信息
一:锁机制存在的性能问题?
在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁(后面的章节还会谈到锁)。
锁机制存在以下问题:
(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
(2)一个线程持有锁会导致其它所有需要此锁的线程挂起。
(3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。
二:volatile的原子性问题?
(1)volatile是不错的机制,但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。
(2)独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
(3)而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
三:CAS 操作?
(1)上面的乐观锁用到的机制就是CAS,Compare and Swap。
(2)CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
四:java的CAS 操作的三大问题。
(1)ABA问题
--->引入版本的思想,则可以解决。
(2)循环时间长,开销大。
--->自旋CAS如果长时间不成功,会给cpu带来非常大的开销。
(3)只能保证一个共享变量的原子操作
--->jdk1.5以后提供AtomicReference类来保证引用对象之间的原子性。可以将多个变量放在一个对象里进行CAS操作
五:cpu处理器如何实现原子操作
(1)使用总线锁保证原子性
--->处理器操作数据,首先先从自己的缓存里读取值,然后修改,再写入系统内存
--->当某个处理器a想修改数据,先向总线发送LOCK#信号,其他处理器b的请求就被阻塞住。该处理器可以独占共享内存。
--->总线锁是把CPU和内存之间的通信锁住了。这使得其他处理器不能操作其他内存地址的数据。所以总线锁的开销很大,目前处理器在某些场合下使用缓存锁定代替总线锁定来进行优化。
(2)使用缓存锁保证原子性
--->一个数据的内存地址,可以被多个cpu缓存,但当其中一个cpuA要对该地址的数据进行修改的时候,其他cpu的缓存失效。其他cpu想要在这个时候对该cpu地址进行进行操作,需要从主内存中读取数据(且在cpuA将修改结果刷回主内存后),否则则阻塞等待。
--->原理可见9cpu缓存一致性协议
(3)两种情况下处理器不会使用缓存锁定。
--->当操作数据不能被缓存在处理器内部,或者操作的数据跨多个缓存行。则处理器会调用总线锁。
--->有些处理器部支持缓存锁定。只能使用总线锁。