您的位置:首页 > 编程语言 > Java开发

Java多线程之ReentrantLock实现原理和源码分析(二)

2018-11-06 16:56 1041 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014730165/article/details/83784243

章节概览、

1、ReentrantLock概述

ReentrantLock字面含义是可重入的互斥锁,实现了和synchronize关键字一样的独占锁功能。但是ReentrantLock使用的是自旋锁,通过CAS硬件原语指令实现的轻量级的锁,不会引起上下文切换。而Synchronize关键字是重量级的且是独占的悲观锁。在使用过程中,会引起上下文切换。同时ReentrantLock增加了一些高级的扩展功能,比如它可以实现公平锁,同时也可以绑定多个Conditon。每个Condition都维护了自己的阻塞队列。而不像wait/notify只维护一个队列而引发的唤醒错误的情况。

2、ReentrantLock的实现模板

ReentrantLock锁保证线程安全的实现模板如下:

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

从默认的实现模板可以看出,其使用方式和Synchronize不同,首先其不需要同步的对象。而Synchronize需要一个同步锁对象。其次ReentrantLock是显示调用。用户自己可以控制从哪里开始,到哪里结束。而Synchronize只能同步代码块等。具体ReentrantLock的使用方式,请参考:可重入锁:ReentrantLock理解使用

3、ReentrantLock类图结构

ReentrantLock的类图结构如下:

从类的继承图上可以看出,ReentrantLock实现了Lock接口。同时其具有3个内部类,分别为Sync,NonfairSync,FairSync。而Sync有继承了AbstractQueuedSynchronize,这个就是我们常说的AQS。而NonFairSync继承Sync也就是我们所说的非公平锁。FairSync继承Sync实现了公平锁功能。

3.1、Lock接口源码描述
/**
* @see ReentrantLock
* @see Condition
* @see ReadWriteLock
*
* @since 1.5
* @author Doug Lea
*/
public interface Lock {
//获取锁
void lock();
//获取可中断锁
void lockInterruptibly() throws InterruptedException;
//尝试获取锁,立刻返回
boolean tryLock();
//在指定的时间范围内尝试获取锁,立即返回
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放当前锁
void unlock();
//获取一个条件对象,用于线程等待唤醒
Condition newCondition();
}
3.2、ReentrantLock的构造方法和成员

ReentranLock的构造方法成员参数如下:

public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
// 同步器的引用
private final Sync sync;
//非公平锁的构造函数
public ReentrantLock() {
sync = new NonfairSync();
}
// 公平锁的构造函数
public ReentrantLock(boolean fair) {
// 三目运算符,如果为true 则为公平锁,反之为非公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
}
3.3、Sync抽象类核心源码分析
// 静态的抽象内部类,修饰符为default,仅能在当前类所在的包中使用
// 继承了AbstractQueuedSynchronizer抽象类
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;

/**
* 抽象方法,获取当前锁,具体实现在子类中实现
*/
abstract void lock();

/**
* Performs non-fair tryLock.  tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
* 非公平锁尝试的去获取锁,立刻返回
*/
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取当前系统的同步状态变量state
int c = getState();
// 判断当前的系统状态变量是否为0
// 其中0,表示当前锁空闲。大于0表示当前系统锁已经被占用
if (c == 0) {
// 利用CAS原语设置当前state的状态值为1,立刻返回
if (compareAndSetState(0, acquires)) {
// 如果设置成功,表示已经获取当前锁,设置当前锁的拥有者为当前线程
setExclusiveOwnerThread(current);
// 返回获取成功
return true;
}
}
// 如果当前是state不为0,判断当前线程是否已经拥有锁
// 因为ReentrantLock是可冲入锁,所以如果当前线程已经拥有锁的情况下,可以再次使用该锁
else if (current == getExclusiveOwnerThread()) {
// 如果当前线程已经拥有锁,则把当前的 state + 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 设置当前state的值
setState(nextc);
// 返回成功
return true;
}
return false;
}
// 释放当前锁资源
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}

// 判断当前线程是否拥有锁
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
// 返回条件对象
final ConditionObject newCondition() {
return new ConditionObject();
}

// Methods relayed from outer class
// 获取当前拥有锁的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 获得当前线程持有的可重入锁的状态值
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 是否获得锁
final boolean isLocked() {
return getState() != 0;
}

/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
3.4、NonfairSync类核心源码分析

NonfairSync主要是非公平锁的实现:

static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;

/**
* Performs lock.  Try immediate barge, backing up to normal
* acquire on failure.
* 实现Sync的方法
*/
final void lock() {
// 尝试获取锁lock() 方法
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 获取锁失败,加入到等待队列
acquire(1);
}

protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
3.5、FairSync类核心源码分析

以下是公平锁实现的核心源码

static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 公平锁没有尝试的去获取锁的功能,直接调用acquire()方法
final void lock() {
acquire(1);
}

/**
* Fair version of tryAcquire.  Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 公平锁中
if (c == 0) {
// 通过hasQueuedPredecessors判断当前线程是否为head的next节点。
// 如果是的话,则尝试获取锁
// 获取锁成功进行一系列的设置
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//可重入锁的设计思想
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}

hasQueuedPredecessors源码分析:

public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}

这里的逻辑判断稍微有些复杂,我们整理下思路。
return返回的代码,可以将逻辑判断分为2部分,只要其中有一个为true,则返回为true。当返回为真的话, if (!hasQueuedPredecessors() && compareAndSetState(0, acquires))就失败。我们详细分析下:

  • h != t && ((s = h.next) == null

这个逻辑成立的一种可能是head指向头结点,tail此时还为null。考虑这种情况:当其他某个线程去获取锁失败,需构造一个结点加入同步队列中(假设此时同步队列为空),在添加的时候,需要先创建一个无意义傀儡头结点(在AQS的enq方法中,这是个自旋CAS操作),有可能在将head指向此傀儡结点完毕之后,还未将tail指向此结点。很明显,此线程时间上优于当前线程,所以,返回true,表示有等待中的线程且比自己来的还早。

  • s.thread != Thread.currentThread()

当前的线程和Head的next线程是否相等。如果不相等返回为true。说明当前线程不是head的next节点,因为在公平锁中,只有head的next节点才有权利去获取锁

4、 结语

至此ReentrantLock的类结构和核心源码做了简单分析。通过分析我们可以了解到ReentrantLock的整体结构,构造函数,以及当前类的成员变量。以及ReentrantLock的内部类的情况,内部类的继承情况。ReentrantLock中使用了大量的内部类,很好的做到了封装性。对细节实现部分进行了封装。用户只需要调用构造方法,而不用去关系其内部的实现情况。同时也是设计模式中的迪米特法则:最少知道原则的体现。实现了很好的高内聚减少了耦合。

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: