Java 锁的类型

Java并发中锁的类型

很多锁并不是指某一种特定的实现,而是一种概念模型,是对锁原理的一种分类

 

乐观锁与悲观锁

乐观锁:

假设不会发生并发问题,先进行操作,若检测到线程冲突,采取一些补偿策略来保证同步安全。例如利用CAS进行自旋更新数据就是一种乐观锁策略,其实也是避免阻塞的无锁策略。

悲观锁:

假设并发同步问题一定会发生,即事先就对需要同步的部分加锁,以保证线程安全。互斥同步就是典型的悲观锁。但在实际运行过程中,JVM会对不必要的锁进行优化。在Java中悲观锁的典型就是synchronized。

 

自旋锁与自适应自旋锁

自旋锁是为了解决互斥锁频繁切换线程产生的开销的一种非阻塞设计。

简单的说,就是当线程运行到加锁的代码块时,不会由运行状态切换至等待的状态,而是进行一个不断的循环,当拿到锁时跳出循环并继续运行。

但自旋等待的过程不能一直持续下去,如果自旋时间过长,反而是对cpu资源的一种浪费,这时候可能切换至阻塞状态所造成的开销反而小一点。为了解决这个问题,就有了自适应的自旋锁。

自适应自旋锁会根据上下文自动调整自旋时间,如一个线程常常在很少的几次自旋之后就拿到锁,则可能该线程允许的自旋时间最大值就大一点,若一个线程常常没能在少数次自旋后拿到锁,或者经常需要很长一段时间才能拿到锁,则可能该线程遇到加锁部分时会直接进入阻塞状态。

Jdk1.6之后采用了自适应自旋锁

 

公平锁和非公平锁

公平与否主要是指是否实现了“先申请先拿锁”的原则。

synchronied就是一种非公平锁,后申请锁的线程可能会比先申请锁的线程先拿到锁,这可能会导致某一线程迟迟拿不到锁而挂起很长的时间。但实际上这一块会由JVM控制,不太可能有某一线程持续挂起。

公平锁的一个实现是JUC下的重入锁(ReentrantLock),重入锁默认也是不公平的,可以通过带boolean参数的构造方法创建公平的重入锁,这一功能由AQS(AbstractQueuedSynchronizer)保证。

 

轻量级锁/重量级锁

重量级锁指的是传统互斥锁,即会发生线程状态切换的开销。

轻量级锁在没有发生竞争时通过CAS操作避免状态切换的开销,在没有多线程竞争的情况下性能优于重量级锁。

当一个线程进入同步代码块时,如果该同步对象没有被加锁,则试图向对象加上轻量级锁(这里有一个通过CAS操作对象MarkWord标志位的动作),成功则该线程拿到轻量级锁,失败则检查该同步对象的MarkWord是否指向当前线程,是,则一切正常执行;否则表明在CAS过程中该对象被其他线程占用,该对象的轻量级锁变为重量级锁,将导致后续线程阻塞。

轻量级锁的性能优势在于没有发生线程竞争时避免互斥加锁的开销,只有当发生竞争时才执行互斥加锁的操作(这也是乐观策略的一种应用),但也正因为这种只有发生竞争时才有互斥开销的特点,导致了发生互斥时总是附带了一个检测互斥的CAS操作,这会导致线程竞争时效率低下

 

偏向锁

类似于轻量级锁,也是用于解决互斥开销的一种方案。

轻量级锁会有一个CAS操作来检测竞争,偏向锁在无竞争的时候将不做任何的同步工作,只是修改对象的MarkWord为偏向模式的值,一旦其他线程尝试拿到同步对象的锁,则偏向锁被消除,变为未锁定或者轻量级锁的状态。和轻量级锁一样,用于提高同步对象在没有竞争时的性能。

 

分段锁

典型的应用就是ConcurrentHashMap,只对需要操作的部分字段加锁,而不是像HashTable一样对整个对象加锁。分段锁也不是指的某一种锁,而是一种实现方案。

猜你喜欢

转载自blog.csdn.net/my_dearest_/article/details/80005648
今日推荐