Lock锁子类了解一下

前言

回顾前面:

只有光头才能变强!

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

  • ReentrantLock
  • ReentrantReadWriteLock

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

一、ReentrantLock锁

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

来总结一下要点吧:

  • 比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内部类

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

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

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

1.2构造方法

1.3非公平lock方法

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

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

我们去看看tryAcquire()

1.4公平lock方法

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

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

1.5unlock方法

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

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

二、ReentrantReadWriteLock

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

而ReentrantReadWriteLock是一个读写锁

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

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

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

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

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

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

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

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

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

2.1ReentrantReadWriteLock内部类

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

  • WriteLock
  • ReadLock

2.2读锁和写锁的状态表示

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

2.3写锁的获取

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

进去看看实现:

2.4读锁获取

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

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

三、最后

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

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

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

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

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

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

参考资料:

  • http://cmsblogs.com/?page_id=111

原文发布于微信公众号 - Java3y(java3y)

原文发表时间:2018-04-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏MasiMaro 的技术博文

hook键盘驱动中的分发函数实现键盘输入数据的拦截

我自己在看《寒江独钓》这本书的时候,书中除了给出了利用过滤的方式来拦截键盘数据之外,也提到了另外一种方法,就是hook键盘分发函数,将它替换成我们自己的,然后再...

772
来自专栏JadePeng的技术博客

Docker+Jenkins持续集成环境(5): android构建与apk发布

项目组除了常规的java项目,还有不少android项目,如何使用jenkins来实现自动构建呢?本文会介绍安卓项目通过jenkins构建的方法,并设计开发一个...

4308
来自专栏Android相关

ReentrantLock中的NonfairSync加锁流程

重入锁中的非公平锁,尝试获取锁的线程有可能会成功,如果不成功的话,则会进入AQS的队列中。

664
来自专栏张善友的专栏

ASP.NET Web API 接口执行时间监控

软件产品常常会出现这样的情况:产品性能因某些无法预料的瓶颈而受到干扰,导致程序的处理效率降低,性能得不到充分的发挥。如何快速有效地找到软件产品的性能瓶颈,则是我...

2298
来自专栏不会写文章的程序员不是好厨师

利用LockSupport实现互斥锁和共享锁

首先说说LockSupport吧,它的作用是提供一组直接block或unblock线程的方法,其底层实现利用了Unsafe(前面文章有讲过Unsafe)。Loc...

1222
来自专栏西二旗一哥

iOS - autoreleasepool and @autoreleasepool

+ 在一个自动引用计数的环境中(并不是垃圾回收机制),一个包含了多个对象的 NSAutoreleasePool 对象能够接收 autorelease 消息并且...

1154
来自专栏青青天空树

趣味题:恺撒Caesar密码(c++实现)

描述:Julius Caesar 生活在充满危险和阴谋的年代。为了生存,他首次发明了密码,用于军队的消息传递。假设你是Caesar 军团中的一名军官,需要把Ca...

602
来自专栏wannshan(javaer,RPC)

AbstractQueuedSynchronizer 源码分析

AQS是通过CHL队列来实现锁请求阻塞列表的。可以通过acquire(int arg)来分析,当前线程竞争锁时的流程,然后再通过release(int arg)...

3466
来自专栏JackieZheng

探秘Tomcat——启动篇

tomcat作为一款web服务器本身很复杂,代码量也很大,但是模块化很强,最核心的模块还是连接器Connector和容器Container。具体请看下图: ? ...

4077
来自专栏FreeBuf

scapy在wlan中的应用

Scapy 又是scapy,这是python的一个网络编程方面的库,它在wlan中也有很强大的应用。一般我们买块网卡,然后aircrack-ng套件爆破一下邻居...

25210

扫码关注云+社区