Java并发问题处理-----乐观锁和悲观锁

1.乐观锁 : 就是每次去拿数据的时候认为别人不会去修改,所以不会上锁;但是,在更新数据的时候会去检查别人有没有修改数据,可以使用版本号或者是时间戳机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。像数据库提供的类似write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。乐观锁认为数据在一般情况下是不会产生并发冲突的,只是在对数据执行更新操作时才会去正式检查数据是否发生了并发冲突,如果发生了并发冲突,则返回用户错误信息,让用户决定如何去做。

2.悲观锁 :就是每次去拿数据的时候都认为别人会去修改数据,所以每次都会上锁,这样就导致别人想拿数据就会一直阻塞直到拿到锁。传统的数据库里有很多都用到了这种锁机制,例如:行锁、表锁、读锁、写锁等,都是在做操作之前先上锁。再比如Java中的同步锁关键字synchronized的实现也是悲观锁。

3.乐观锁的一种实现机制----CAS(Compare And Swap 比较并交换)

          锁存在的问题:

           Java在JDK1.5之前是靠synchronized这个关键字保证同步的,这种使用一致的锁定协议来协调对共享状态的访问,可以保证无论那个线程持有共享变量的锁,都采用独占的方式来访问这些变量。这就是一种独占锁,独占锁其实就是悲观锁。所以说,synchronized就是悲观锁

         悲观锁机制存在以下问题:

         1.在多线程的竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

         2.一个线程持有锁会导致其他想拥有该锁的线程挂起。

         3.一个优先级高的线程在等待一个优先级低的线程释放锁会导致优先级导致,引起性能风险。

对于悲观锁出现的这些问题一个行之有效的办法就是使用乐观锁,上文已经提到:乐观锁就是每次操作都不加锁并且假设没有并发冲突而去执行该次操作,如果因为并发冲突失败就重试,直到成功为止!

              CAS:

    CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。   

    CAS 操作中包含三个操作数 —— 需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B。否则处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“ 我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。 ”这其实和乐观锁的冲突检查+数据更新的原理是一样的。

    这里再强调一下,乐观锁是一种思想。CAS是这种思想的一种实现方式。

  JAVA对CAS的支持:

    在JDK1.5 中新增 java.util.concurrent (J.U.C)就是建立在CAS之上的。相对于对于 synchronized 这种阻塞算法,CAS是非阻塞算法的一种常见实现。所以J.U.C在性能上有了很大的提升。

猜你喜欢

转载自blog.csdn.net/Hope_lee/article/details/82561612