Lock锁子类了解一下

前言

回顾前面:

只有光头才能变强!

上一篇已经将Lock锁的基础AQS简单地过了一遍了,因此本篇主要是讲解Lock锁主要的两个子类:

  • ReentrantLock
  • ReentrantReadWriteLock

那么接下来我们就开始吧~

一、ReentrantLock锁

首先我们来看看ReentrantLock锁的顶部注释,来看看他的相关特性呗:

img_032887d97e96357b446161eb2846d159.png

来总结一下要点吧:

  • 比synchronized更有伸缩性(灵活)
  • 支持公平锁(是相对公平的)
  • 使用时最标准用法是在try之前调用lock方法,在finally代码块释放锁

class X {
    private final ReentrantLock lock = new ReentrantLock();
    // ...

    public void m() { 
        lock.lock();  // block until condition holds
        try {
            // ... method body
        } finally {
            lock.unlock()
        }
    }
}

1.1内部类

首先我们可以看到有三个内部类:

img_e7638ca0ee85d011a43c310c04b8aa48.png

这些内部类都是AQS的子类,这就印证了我们之前所说的:AQS是ReentrantLock的基础,AQS是构建锁、同步器的框架

  • 可以很清晰的看到,我们的ReentrantLock锁是支持公平锁和非公平锁的~

img_84b2e3e6979cadf83faba48651772c96.png

1.2构造方法

img_afc10d9316d5cd2a6011623ecc3c7957.png

1.3非公平lock方法

尝试获取锁,获取失败的话就调用AQS的acquire(1)方法

img_cd187919a38bc76795d68cfa42e216d0.png

acquire(1)方法我们在AQS时简单看过,其中tryAcquire()是子类来实现的

img_ef777ea037d6571cbb8524732a10643c.png

我们去看看tryAcquire()

img_4b1d3c68f64be84539c7a43b21ab4607.png

1.4公平lock方法

公平的lock方法其实就多了一个状态条件

img_6537797fdef2da0fb18f7e9b50a103d1.png

这个方法主要是判断当前线程是否位于CLH同步队列中的第一个。如果是则返回flase,否则返回true

img_4829dccee535eb5ac64566d986de3cf4.png

1.5unlock方法

img_c1a366cdba1444357880d667be96646c.png

unlock方法也是在AQS中定义的:

img_90a19b26fb2a2a0d7142476e30c1d126.png

去看看tryRelease(arg)是怎么实现的:

img_7773f91e7748b3e4ded0469d41cbef04.png

二、ReentrantReadWriteLock

我们知道synchronized内置锁和ReentrantLock都是互斥锁(一次只能有一个线程进入到临界区(被锁定的区域))

而ReentrantReadWriteLock是一个读写锁

  • 取数据的时候,可以多个线程同时进入到到临界区(被锁定的区域)
  • 数据的时候,无论是读线程还是写线程都是互斥

一般来说:我们大多数都是读取数据得多,修改数据得少。所以这个读写锁在这种场景下就很有用了!

读写锁有一个接口ReadWriteLock,定义的方法就两个:

img_6faa31c38c4e7e695cd769240459f82a.png

我们还是来看看顶部注释说得啥吧:

img_97f74b27cc34a100f83636d24bf5ea80.png

其实大概也是说明了:在读的时候可以共享,在写的时候是互斥的

接下来我们还是来看看对应的实现类吧:

img_ebaeefe94b5d747f304da4c443f199e3.png

按照惯例也简单看看它的顶部注释:

img_8ea3d6a711c5ba65e1cb40bad12583ca.png

于是我们可以总结出读写锁的一些要点了:

  • 读锁不支持条件对象,写锁支持条件对象
  • 读锁不能升级为写锁,写锁可以降级为读锁
  • 读写锁也有公平和非公平模式
  • 读锁支持多个读线程进入临界区,写锁是互斥的

2.1ReentrantReadWriteLock内部类

ReentrantReadWriteLock比ReentrantLock锁多了两个内部类(都是Lock实现)来维护读锁和写锁,但是主体还是使用Syn

  • WriteLock
  • ReadLock

img_729e56c43b788951f8cd5dd5383cdea8.png

2.2读锁和写锁的状态表示

在ReentrantLock锁上使用的是state来表示同步状态(也可以表示重入的次数),而在ReentrantReadWriteLock是这样代表读写状态的:

img_a866ddd706ce356b753e301a3c06cc6e.png

2.3写锁的获取

主要还是调用syn的acquire(1)

img_48019ed7cc3044a13a7ecf3fd17bd3e4.png

进去看看实现:

img_0b514a8e2c1b0e553474c0cf077d7feb.png

2.4读锁获取

写锁的获取调用的是acquireShared(int arg)方法:

img_c99d940ae2983362cbbb757bb5ac979f.png

内部调用的是:doAcquireShared(arg);方法(实现也是在Syn的),我们来看看:

img_273ec7bca86bac9214b9559ccb917036.png

三、最后

这里就简单总结一下本文的内容吧:

  • AQS是ReentrantReadWriteLock和ReentrantLock的基础,因为默认的实现都是在内部类Syn中,而Syn是继承AQS的~
  • ReentrantReadWriteLock和ReentrantLock都支持公平和非公平模式,公平模式下会去看FIFO队列线程是否是在队头,而非公平模式下是没有的
  • ReentrantReadWriteLock是一个读写锁,如果读的线程比写的线程要多很多的话,那可以考虑使用它。它使用state的变量高16位是读锁,低16位是写锁
  • 写锁可以降级为读锁,读锁不能升级为写锁
  • 写锁是互斥的,读锁是共享的

总的来说看多线程源码难度系数还是好高啊,我目前的水平只能过一过了....

多线程后面还有挺多高深的知识点:Future、同步容器啊、阻塞队列、各种原子类啊等等等,这里我打算就先放一放了,目前的水平有限啊~~~~~

后面可能会有一篇线程池的博文,敬请期待咯~

有兴趣的同学可继续往下面的参考资料下学习~~~

参考资料:

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

文章的目录导航

更多的文章可往: 文章的目录导航
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值