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

java.util.concurrent.locks.ReentrantLock重入锁源码解析

2017-09-07 10:01 1211 查看

1.什么是重入锁

重入锁就是指重复进入锁,它表示该锁能够支持一个线程对资源的重复加锁。

2.重入锁所属包

package java.util.concurrent.locks;


3.重入锁继承与实现关系

public class ReentrantLock implements Lock, java.io.Serializable


4.重入锁的自定义同步器源码

重入锁ReentrantLock采用继承的AQS的自定义的同步器来实现的。在自定义同步器的基础上实现的公平锁和非公平锁的。

自定义同步器的实现类:

abstract static class Sync extends AbstractQueuedSynchronizer


自定义同步器实现方法:


nonfairTryAcquire() 方法:在独占模式、非公平模式下尝试获取同步状态

/**
* 在独占模式,非公平模式下尝试获取同步状态
*/
final boolean nonfairTryAcquire(int acquires) {
//获取当前的线程current
final Thread current = Thread.currentThread();
//原子的方式获取当前同步状态值
int c = getState();
//如果同步状态值为初始化状态
if (c == 0) {
//如果当前同步状态值等于预期值0,那么就将以原子的方式将当前同步状态值设置为更新值acquires
if (compareAndSetState(0, acquires)) {
//设置拥有独占访问权限的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前线程为拥有独占访问权限的线程,这个代码块是当前线程重入情况下,同步状态值增加acquires
else if (current == getExclusiveOwnerThread()) {
//当前同步状态值加上获取对象本身的状态值
int nextc = c + acquires;
//如果最新同步状态值小于0,抛出最大锁数量超出
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//原子的方式设置最新同步状态值
setState(nextc);
return true;
}
return false;
}
步骤解析:

<1>首先获取当前线程,如果当前同步状态值为初始化状态,就设置初始状态值(acquires值),设置独占访问线程的权限为当前线程,返回true。

<2>如果当前同步状态值已经被设置过了并且独占访问线程的权限就是当前线程(锁重入的情况了),那么就将原本的同步状态值加上锁重入对象的状态值作为最新同步状态值,然后设置同步状态值,返回true。

<3>如果未设置成功并且当前线程不是获取锁的线程(独占式嘛),就返回false了。

tryRelease() 方法:在独占模式下,尝试释放同步状态。

//在独占模式下,尝试释放同步状态
protected final boolean tryRelease(int releases) {
//原子方式获取当前同步状态值减去对象本身已经获取要释放的状态值
int c = getState() - releases;
//如果当前线程不是拥有独占访问权限的线程,抛出非法主机状态异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//释放标志位默认为false
boolean free = false;
//如果释放了的同步状态值为初始化状态
if (c == 0) {
//设置释放标志位为true
free = true;
//设置拥有独占访问权限的线程为null
setExclusiveOwnerThread(null);
}
//原子的方式更新最新的同步状态值
setState(c);
//返回未完全释放状态值的标志位false
return free;
}
步骤解析:

<1> 获取释放状态值之后得到最新的同步状态值 c。

<2> 如果释放同步状态值为初始化状态(说明没有获取锁资源的线程了),则设置释放标志为true,设置拥有独占访问权限的线程自然也就是null了。

<3>更新同步状态值为最新的同步状态值 c,返回释放标志位的值。

其他方法:

//当前的线程是否是以独占的方式进行的
protected final boolean isHeldExclusively() {
//拥有独占访问权限的线程与当前线程比较,是否为同一个
return getExclusiveOwnerThread() == Thread.currentThread();
}

//获取当前同步状态值
final int getHoldCount() {
//如果当前线程是以独占的方式运行,则得到当前同步状态值,否则为0(初始化的值)
return isHeldExclusively() ? getState() : 0;
}


5.什么是公平锁和非公平锁

如果对锁先请求的线程先被满足就是公平锁,而对锁先请求没有得到满足,满足条件是随机的就是不公平锁。这么一想,那么对于公平锁,等待时间最长的线程最有可能获取锁,所以从此也会看出公平锁是有顺序获取的。

6.非公平锁的源码

/**
* 独占模式下非公平锁的实现
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;

/**
* 当前线程获取锁资源
*/
final void lock() {
//如果当前同步状态值等于预期值0,也就是说处于初始化的状态,那么就将当前同步状态值更新为1
if (compareAndSetState(0, 1))
//设置拥有独占访问权限的线程为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
//以独占的模式获取对象,使用AQS的acquire方法。
acquire(1);
}

//是否允许在独占模式、非公平锁的方式下获取对象状态
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}


非公平锁实现关键:

/**
* 在独占模式,非公平模式下尝试获取同步状态
*/
final boolean nonfairTryAcquire(int acquires) {
//获取当前的线程current
final Thread current = Thread.currentThread();
//原子的方式获取当前同步状态值
int c = getState();
//如果同步状态值为初始化状态
if (c == 0) {
//如果当前同步状态值等于预期值0,那么就将以原子的方式将当前同步状态值设置为更新值acquires
if (compareAndSetState(0, acquires)) {
//设置拥有独占访问权限的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前线程为拥有独占访问权限的线程,这个代码块是当前线程重入情况下,同步状态值增加acquires
else if (current == getExclusiveOwnerThread()) {
//当前同步状态值加上获取对象本身的状态值
int nextc = c + acquires;
//如果最新同步状态值小于0,抛出最大锁数量超出
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//原子的方式设置最新同步状态值
setState(nextc);
return true;
}
return false;
}


nonfairTryAcquire 流程图:



7.公平锁的源码

/**
* 独占模式下公平锁的实现
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;

//独占模式,当前线程获取公平锁
final void lock() {
acquire(1);
}

/**
* 在独占模式下,公平锁的方式下获取对象状态
*/
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//原子的方式获取当前同步状态值
int c = getState();
//如果当前同步状态值为0,也就是初始化状态
if (c == 0) {
//如果不存在等待时间更长的线程(就是同步队列中当前节点是否有前驱节点)并且当前同步状态值为初始化状态(更新同步状态值为acquires)
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//设置拥有独占访问权限的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
//如果拥有独占访问权限的线程是当前线程
else if (current == getExclusiveOwnerThread()) {
//计算最新的同步状态值
int nextc = c + acquires;
//如果最新的同步状态值小于0,那么抛出超出最大锁的数量
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//原子的方式设置最新的同步状态值
setState(nextc);
return true;
}
return false;
}
}


tryAcquire 流程图:



8.重入锁的构造方法

/**
* 实例化一个独占模式下的非公平锁的对象
* 默认构造方法是采用非公平锁方式
*/
public ReentrantLock() {
sync = new NonfairSync();
}

/**
* 如果传入的参数fair为true就创建公平锁,否则就创建非公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}


9.重入锁的方法

lock 方法:获取锁

public void lock() {
sync.lock();
}


tryLock 方法:

/**
* 查看该锁没有对其他线程持有,是则返回true,否则为false
*/
public boolean tryLock() {
//自定义同步器的非公平方式获取同步状态
return sync.nonfairTryAcquire(1);
}


unlock 方法:

/**
* 尝试释放锁
*/
public void unlock() {
sync.release(1);
}


10.阅读总结

(1)ReentrantLock采用自定义同步器继承AQS的方式来实现的。

(2)ReentrantLock分为公平锁和非公平锁两种获取锁的方式。

(3)ReentrantLock重入锁中线程重进入的过程:就是先查看获取锁的线程是否为当前占据锁的线程,是就先获取它,然后采用当前占据锁的线程的同步状态值加上重进入的当前线程对象的同步状态值作为最新同步状态值来以原子的方式来更新当前同步状态值。

(4)ReentrantLock重入锁释放锁的过程:原子的方式获取当前同步状态值减去要释放的同步状态值的结果为0,说明为初始化状态了,就释放成功。(释放锁成功的条件就是最终的同步状态值为0)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐