JDK源码研究——ReentrantLock浅析

本文深入分析了JDK1.5引入的ReentrantLock,讲解了其核心源码,包括整体结构、公平锁与非公平锁的概念,并对比了ReentrantLock与synchronized的差异。通过对ReentrantLock的公平性和性能等方面的讨论,帮助读者理解其在并发编程中的应用。
摘要由CSDN通过智能技术生成

从今天开始,要执行自己的学习计划了!!写正文之前,先发一下牢骚。前几天租的地方断网了,说是要进行城中村网络线路改造,把原来的所有的网线都暴力剪断了!搞得好几天上不了网。现在没有网络,生活中总感觉缺少点什么东西。虽然上网也干不了什么东西,但就是会觉得比较烦闷。以前没有网络的时候不也好好的嘛,所以说,互联网真的是已经深刻地改变了我们的生活,已经成为了生活中必不可少的东西。谷物是生活食量,而网络就是精神食量了。现在貌似也没有听说过“网瘾”这个词了,这个词在早些年可是一个标准的贬义词来着。看来人们的思维也是在时刻发生着变化的。废话有点多,进入正题!

本文简单地介绍一下从JDK1.5开始引入的java.util.concurrent(简称J.U.C)包下的ReentrantLock类。Reentrant的英文含义是“可重入的”,也就是说ReentrantLock表示可重入的锁。这个类是用纯的java语言来实现synchronized关键字的功能,并且补充了synchronized没有实现的部分功能。由于能力有限,只能从浅层次来对ReentrantLock进行分析。本文的主要内容如下:

  1. 浅析ReentrantLock的核心源代码;
  2. 解释一下自己所理解的公平锁和非公平锁;
  3. 把ReentrantLock和synchronized做一下简单地对比。

1 ReentrantLock的核心源代码

首先,感觉源代码的分析工作实在是不好做。贴太多代码吧,让人看得昏昏欲睡;不贴吧,光用文字和图片又说不太清楚。太深入吧,代码一层套一层,讲完
下层还得回到上层,反正我是理解不了了。准备以自己看代码的顺序和思维方式来讲讲ReentrantLock的源代码(JDK1.7),大致的分析顺序为:

  1. 代码的整体结构
  2. 类的javadoc要点
  3. 核心内部类和方法(调用层次不超过3层)。
1.1 ReentrantLock类的整体结构

首先先看一下ReentrantLock类的继承结构。类的签名如下:

public class ReentrantLock implements Lock, java.io.Serializable

ReentrantLock类实现Serializable接口,表示这个类是可以序列化和反序列话的,也就是说ReentrantLock对象可以保存到硬盘中,通过网络传输,或者其他的其他方式。实现了Lock接口表示这是锁的一种,Lock接口是一个独立的接口,没有继承其他接口。它定义了所有锁的一系列基本操作:

void lock(); // 尝试获取锁,如果没有成功,则阻塞当前线程。

void lockInterruptibly() throws InterruptedException;

boolean tryLock(); // 尝试获取锁,如果不成功,则直接放弃锁,并返回false。成功的话,则加锁,并返回true。

boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 尝试获取锁,然后等待time时间后仍然没有成功,则返回false。

void unlock(); // 释放锁

Condition newCondition(); // 创建新的Condition实例。Condition是通过Java代码实现object.wait(),object.notify()和object.notifyAll()的功能。
1.2 ReentrantLock类javadoc要点

Javadoc是JDK的重要的资料和资源,通常,类和方法的一些重要信息都会在里面提及。Javadoc里面的英文还算比较好懂,是比较经典的技术文档的写好。认真研究这些Javadoc可以提高自己的文档注释能力。写一些优雅的文档和注释,应该是一个IT人员的基本素养。写好注释和文档,你好我好他也好!

下面尝试着翻译一下ReentrantLock类javadoc。

synchronized方法或申明可以隐式地监视锁,可重入锁除了具备与其相同的基本行为和语义外,还附加了其他功能。

本类的构造函数能够接收一个fairness参数。如果这个参数被设置为true,则锁倾向于授予等待最久的那个线程。否则,不保证任何特定的访问顺序。如果程序中使用公平锁,当大量线程访问锁时,其吞吐量通过远小于使用非公平锁。但却有更小的时间间隔(两个线程获得锁的时间差),并且可以保证不会出现线程饥饿。然而,公平锁并不能保证线程调度的公平性。这样的话,有可能会出现同一个线程多次成功获得锁,而另外的活动线程却无法继续运行,并且当前没有持有锁的情况。

一种值得推荐的用法是:在调用lock方法后,立刻接上try代码块。典型的用法如下:

class X {
    private final ReentrantLock lock = new ReentrantLock();
    // ...
    public void m() {
        lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
}

除了实现了Lock接口外,本类还定义了isLocked和getLockQueueLength方法。此外,还定义了一些相关的protected级别的方法,方便记录和监控。

这种锁支持被同一个线程锁定的最大重数为2147483647。试图超过这个限制,会导致锁定时出现错误。

1.3 核心内部成员和方法分析

下面列出重要的成员变量(类)和方法。

private final Sync sync;  // 私有的同步器类,这个类是ReentrantLock定义的内部类。final表示锁的性质一旦确定,不可更改。

abstract static class Sync extends AbstractQueuedSynchronizer // 包级别的内部静态类,它集成的AbstractQueuedSynchronizer(简称A.Q.S)类是整个J.U.C包的基础,定义了带队列的同步的器的功能。它有两个子类NonfairSync(非公平同步器)和FairSync(公平的同步器),

static final class NonfairSync extends Sync; // 非公平同步器

static final class FairSync extends Sync; // 公平同步器

如果上面的第1行代码中sync指向NonfairSync实例,表示锁是非公平锁,如果指向FairSync实例,则表示锁为非公平锁。它们的区别会在接下的内容详细解释。下面详细地分析ReentrantLock类中的主要方法,如果可以,也会试着按照自己的理解来解释为什么要这么实现。

(1) lock()方法

这个方法直接调用sync.lock()方法,这是在Sync类的一个抽象方法,需要在子类中实现。NonfairSyncFairSyncSync的两个实现类,lock()方法的实现就体现了这个类的差异。首先看下NonfairSync的lock()方法,该方法的源代码如下:

final void lock
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值