面时莫慌 | 深入分析JUC之Lock接口


theme: jzman

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」

前面两篇文章全面分析了synchronizedvolatilesynchronized通过隐式地获取锁和释放锁满足了对共享资源访问的原子性,可见性,顺序性。而volatile借助CPU的MESI协议,并通过读写都操作主内存方式满足了对共享资源访问的可见性,有序性。有这两个关键字,是能满足绝大部分并发场景,但是不是它们两个就够了?当然不行,从这篇文章开始,我将一步一步的走进传说中JUC,那就首先来了解一下JUC下核心的接口Lock吧。

一、Lock出世

对于volatile,是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性,使用场景十分有限。

对于synchronized,比volatile更重一点,但提供更多的功能,不过也有缺陷。

  • 一个线程获取了对应的锁,除非线程任务执行完成和异常中断,其它阻塞的线程会一直无限期地等待下去。
  • 读写场景,对于锁,读写也是互斥的。
  • 无法感知线程是否获取到锁。
  • 没有超时释放锁的机制,也没有手动释放锁的机制。

针对上面提出的问题,大名鼎鼎的Doug Lea开发了并发场景下使用组件,即并发包java.util.concurrent,其中包含了线程池、阻塞队列、计时器、同步器、并发集合,当然也包括核心组件Lock

Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements.

Java源码对Lock 的介绍第一句如上所示,很清晰地表达了Locksynchronized提供了更加广泛的锁操作,也就是说更加强大了。确实,从Java5以后,Lock的出现解决了synchronized的一些短板,使用起来更加灵活。

二、Lock简述

Lock是一个接口,定义了释放锁和获取锁等抽象方法。如下图所示。

面时莫慌 | 深入分析JUC之Lock接口_第1张图片

定义成接口意味着这个是一套标准的规范,基于Lock实现很多类型的锁,如下图所示。

面时莫慌 | 深入分析JUC之Lock接口_第2张图片

2.1 ReentrantLock

利用AQS实现的一种可重入锁,属于互斥锁。可以从UML类图上可以清晰的看到,它是唯一一个实现了Lock接口的类。所谓重入,线程获取锁以后,再次获取锁不需要阻塞,而是直接在锁计数器上+1。它支持锁中断,具体实现有包括公平锁和非公平锁两种。

2.2 ReentrantReadWriteLock

可重入的读写锁,可以从UML类图上可以清晰的看到,它实现了

ReadWriteLock接口,这个接口提供了两个抽象方法,一个是ReadLock,另一个是WriteLock,如下图所示,两个方法的返回都是Lock接口。

面时莫慌 | 深入分析JUC之Lock接口_第3张图片

读写锁对加锁场景业务的细分,适合的是读多写少的场景,具有以下原则:

  • 读读不互斥
  • 读写互斥
  • 写写互斥

凡是涉及到并发下的写都会存在互斥。

2.3 StampLock

这个锁很特殊,是JDK8引入的新的锁机制。是对读写锁的改进。ReentrantReadWriteLock 在读写场景下互斥的,如果读线程过多,可能会使写线程长期获取不到锁,处于饥饿的状态。StampLock的实现是基于类似于CAS操作的死循环反复尝试的策略,具体的实现是CLH锁,它是一种自旋锁,可保证没有饥饿发生,并且可以保证FIFO的服务顺序。


哥佬倌,莫慌到走!觉好留个赞,探讨上评论。欢迎关注面试专栏面时莫慌 | Java并发编程,面试加薪不用愁。也欢迎关注我,一定做一个长更的好男人。

你可能感兴趣的:(java,多线程,并发编程,分布式,编程语言)