java多线程系列3:悲观锁和乐观锁

1.悲观锁和乐观锁的基本概念

悲观锁:

总是认为当前想要获取的资源存在竞争(很悲观的想法),因此获取资源后会立刻加锁,于是其他线程想要获取该资源的时候就会一直阻塞直到能够获取到锁;

在传统的关系型数据库中,例如行锁、表锁、读锁、写锁等,都用到了悲观锁。还有java中的同步关键字Synchronized也是一种悲观锁;

乐观锁:

总是认为当前想要获取的资源不存在竞争(很乐观的想法),因此在获取资源后,并不会加锁;

但是在执行更新操作时,会判断在这期间是否有其他人更新过这个数据,可使用版本号等机制实现;

适用于多读的应用程序,可提高吞吐量;

像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

2.乐观锁的一种实现方式:CAS

  因为乐观锁的思想是:在通常情况下都认为不会产生并发冲突,因此在对数据进行提交更新的时候,会对将要提交更新的数据进行并发冲突检测、如果冲突存在,则会返回错误信息给用户,让用户决定处理方式。

  基于乐观锁的思想,我们可以知道乐观锁实现的步骤包含两个部分:冲突检测和数据更新,而CAS就是其中一个典型的实现方式.

  CAS:Compare And Swap(比较并交换)

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

    CAS包含三个操作数:

    

java多线程系列3:悲观锁和乐观锁

  在JDK1.5中新增的java.util.concurrent包中的内容就是建立早CAS基础之上的,相对于Synchronized的阻塞式算法,CAS其实是一种非阻塞算法的实现,因此java.util.concurrent包中组件的性能大大提升。

  下面以java.util.concurrent中的AtomicInteger的getAndIncrement(该操作相当于变量自加) 为例,看一下在不加锁的情况下,如何保证线程安全:

 

public class AtomicInteger extends Number implements java.io.Serializable { private volatile int value; public final int get() { return value; } public final int getAndIncrement() { //自旋方式采用CAS来修改当前值,直到成功为止 for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzfxg.html