一、关于ReetrantLock
在上篇文章Java并发编程之AQS中,比较详细的说了一下关于AQS的设计和AQS的代码相关原理。在上篇文章中也说了,AQS是J.U.C的核心,是用来构建锁或其他同步组件的基础框架,这其中就包括了ReentrantLock。由于前面已经详细的说了AQS的原理,在本篇文章中,对于涉及到AQS相关的东西,便会一带而过。
ReentrantLock和synchronized一样都是具有重入的功能,但相比synchronized而言,ReentrantLock的功能显得更丰富。从上面图片中,可以知道ReentrantLock实现了Lock接口和Serializable,则说明ReentrantLock具有Lock接口的所有实现和序列化的功能。从下面图片中,同样可以看出关于Lock接口的所有功能。
上面说了关于ReentrantLock的相关设计,那么下面就开始ReentrantLock的主要实现吧!
二、ReetrantLock的实现
在说ReentrantLock具体实现之前,先大体说下关于ReentrantLock的一个简单的工作流程:
- 在ReentrantLock中同样是维护着一个state变量(因为ReentrantLock类中有一个Sync抽象类,而Sync正是继承着了AbstractQueuedSynchronizer,也就是我们上篇文章所说的AQS,具体的后面会详说),这个state的初始化值为0,表示未锁定状态。
- A线程lock()时,会调用tryAcquire()(公平与非公平都是如此)独占锁,并将state+1
- 此后,其他线程再调用tryAcquire()的时候就会失败,直到A线程unlock()到state=0(即释放锁)为止,其他线程便可以获取该锁了
- 当然,在释放之前,A线程自己可以重复获取此锁的(state变量的累加),这就是重入的概念
- 但要注意,获取多少次就必须要释放多少次,这样才能保证state恢复到0的状态
简单介绍完ReentrantLock的工作流程,现在开始正式进入到ReentrantLock类中的代码解析了。
private final Sync sync;
在ReentrantLock类中,有一个核心的成员变量(如上代码所示),这是一个同步的变量,前面亦曾提及。这个类使用abstract修饰着,这证明着它的下面肯定会有这具体的实现,这个类的具体实现由两种方式:FairSync和NonfairSync(如下图所示)。这说明在使用ReentrantLock的时候,可以添加一个参数,来说明你是使用公平锁的方式还是使用非公平锁的方式。
从上图中,可以看到Sync类继承了AQS,既然继承了AQS,也就说明Sync 具有AQS的所有的特性。接下来看下Sync类的具体代码:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 抽象类,具体实现有fair和nonfair
*/
abstract void lock();
/**
* 非公平锁尝试获取许可的方法
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
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() {
// 判断当前线程是否与getExclusiveOwnerThread()相等,如果相等则是,反之不是。
return getExclusiveOwnerThread() == Thread.currentThread();
}
//
final ConditionObject newCondition() {
return new ConditionObject();
}
// 获取线程本身,首先getState() == 0,就返回一个null,不等0就放回当前这个独占的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 判断是否上锁
final boolean isLocked() {
return getState() != 0;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
在说完ReentrantLock顶层的一些实现后,现在开始说一下它的具体的一些实现了。
公平锁
公平锁的UML结构图如下:
ReentrantLock reentrantLock = new ReentrantLock(true);
// 因为传入的是true,所以会new FairSync()
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 公平锁,默认传值为1
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) {
// 把当前线程赋值给current
final Thread current = Thread.currentThread();
// 获取state
int c = getState();
// 如果等于0,说明当前没有任何线程去获取资源,那么就可以去获取锁了
if (c == 0) {
/**
* 1.hasQueuedPredecessors():判断这个队列里前面有没有排队等待的线程,如果
* 有等待则返回true,反之为false
* 2.如果前面没有等待的线程,那么就可以继续调用CAS操作,这个操作就是对state进
* 行+1,CAS操作是保证多线程操作不会产生线程安全问题的,如果CAS操作成功,将
* 会进行下一步
* 3.调用setExclusiveOwnerThread方法,设置当前线程为独占的
*/
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程引用和当前的线程相匹配,说明是同一个线程,就可以再次获取这把锁(重入)
else if (current == getExclusiveOwnerThread()) {
// 将c和新传入的中进行相加
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 将nextc赋值到state变量中
setState(nextc);
return true;
}
return false;
}
}
上面代码中,如果要加锁的话会调用acquire(1)方法,这里是直接传入1的,因为这个FairSync 是继承自Sync的,而Sync又是继承自AQS的,所以这里的acquire方法时调用AQS中的方法的,看下面的代码一中的代码,这里首先会调用!tryAcquire(arg)方法尝试获取锁,如果获取成功直接调用selfInterrupt()方法(这个方法的作用是打断自己本身),如过获取成功,那将应该打断自己唤醒自己,继续去做一些 后续的事情。如果没有获取成功,那肯定是处于排队等待状态。当然没有获取成功它将会首先去调用addWaiter(Node.EXCLUSIVE),把当前这个线程加入到FIFO双线链表队列中,并且设置这个Node为EXCLUSIVE(独占的),然后调用acquireQueued方法使线程在等待队列中获取资源(自旋),一直获取到资源后才返回。这段代码具体可以去看上篇文章。
对于上面说到的!tryAcquire(arg)方法,如果点进去,你会发现如代码二中所示,这里会说不做具体实现,这里肯定是交由子类去实现的,因为这里说的是公平锁,那么tryAcquire方法的实现,就是上面那段代码中的tryAcquire方法了。
代码一:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
代码二:
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
非公平锁
非公平锁的UML结构图如下:
说完了公平锁,下面来说下非公平锁,其实在ReentrantLock中默认是为非要公平锁的,只有在新建ReentrantLock时传入true时才为公平锁。下面首先看下代码:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
相比较公平锁而言,公平锁的实现,是简单的多了,在非公平锁的代码中,如果当前是非公平锁,就会直接进行CAS操作,就相当于当前线程进来后,不需要排队了,直接进行CAS操作,哪个线程CPU优先分配给它了,就直接对它进行原子操作。也就是谁成功了,谁就去获得这把锁,然后就把当前线程设置为独占。
获取锁
一般获取锁的方式如下:
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
lock()方法:
public void lock() {
sync.lock();
}
前面说了Sync是ReentrantLock中的一个抽象内部类,而且他继承了AQS,且有两个子类FairSync和NonfairSync。则说明ReentrantLock中的大部分功能是由Sync去实现的。在Sync中定义了内部方法lock(),如图:
对于公平锁和非公平锁具体的获取方式前面已经进行说明了,这里就不再进行说明了。
锁释放
获取锁之后,自然是要进行释放的,释放方法如下:
public void unlock() {
sync.release(1);
}
这里释放锁的还是调用Sync中的release,release(1)方法的具体代码如下:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
在上面的代码中,与获取锁的方法相似,也会首先调用tryRelease(int arg):
protected final boolean tryRelease(int releases) {
// 首先用getState()到的值减去传入的releases
int c = getState() - releases;
// 然后判断,如果当前线程不是被独占的,就抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//
boolean free = false;
if (c == 0) {
free = true;
// 将独占置为空
setExclusiveOwnerThread(null);
}
// 把减完后的值赋给state
setState(c);
return free;
}
ReentrantLock与synchronized的区别
区别如下:
- synchronized是关键字,ReentrantLock是类
- ReentrantLock可以获取锁的等待时间进行设置,避免死锁
- ReentrantLock可以获取各种锁的信息
- ReentrantLock可以灵活的实现多路通知
- 机制:sync操作Mark Word,lock调用Unsafe类的park()方法
版权声明:尊重博主原创文章,转载请注明出处:IT虾米网
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/19410.html