一、前言
在上文中谈到了AQS是Lock实现的前提,而本文说到的ReetrantLock就是在此基础上处理的。而本文中会对ReetrantLock公平性,可重入性等进行介绍。
二、特性
2.1 可重入
可重入性也就是能够让线程多次进行锁的获取操作,首先举个例子:
public class ReentranDemo {
private ReentrantLock lock = new ReentrantLock();
public void putDemo(){
try {
lock.lock();
System.out.println("putDemo"+Thread.currentThread().getName());
reInDemo();
}finally {
lock.unlock();
}
}
public void reInDemo(){
lock.lock();
try {
System.out.println("reInDemo"+Thread.currentThread().getName());
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
new Thread(){
public void run(){
new ReentranDemo().putDemo();
}
}.start();
}
}
运行结果
putDemoThread-0
reInDemoThread-0
如果ReentrantLock不是可重入锁的话,那么该线程会进入死锁。
2.2 公平性
公平锁:线程获取锁资源的顺序为先后调用lock方法的顺序依次获取锁
非公平锁:加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待
看个例子:
public class FairLockDemo {
private static Lock fairLock = new ReentrantLock(true);
public void fair() {
for (int i = 0; i < 5; i++) {
new Thread(){
public void run(){
for (int i = 0; i < 5; i++) {
fairLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "is locking " );
} finally {
fairLock.unlock();
}
}
}
}.start();
}
}
public static void main(String[] args) {
FairLockDemo fairLockDemo = new FairLockDemo();
fairLockDemo.fair();
}
}
返回结果如下:
Thread-0is locking
Thread-1is locking
Thread-2is locking
Thread-3is locking
Thread-0is locking
Thread-4is locking
Thread-1is locking
Thread-2is locking
Thread-3is locking
Thread-0is locking
Thread-4is locking
Thread-1is locking
Thread-2is locking
Thread-3is locking
Thread-0is locking
Thread-4is locking
Thread-1is locking
Thread-2is locking
Thread-3is locking
Thread-0is locking
Thread-4is locking
Thread-1is locking
Thread-2is locking
Thread-3is locking
Thread-4is locking
如例子所示,连续获取的情况基本没有
2.3 非公平锁
加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待
看个例子:
public class UnfairLockDemo {
private static Lock unfairLock = new ReentrantLock();
public void unfair() {
for (int i = 0; i < 5; i++) {
new Thread(){
public void run(){
for (int i = 0; i < 5; i++) {
unfairLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "is locking " );
} finally {
unfairLock.unlock();
}
}
}
}.start();
}
}
public static void main(String[] args) {
UnfairLockDemo unfairLockDemo = new UnfairLockDemo();
unfairLockDemo.unfair();
}
}
返回结果如下:
Thread-0is locking
Thread-3is locking
Thread-3is locking
Thread-3is locking
Thread-3is locking
Thread-4is locking
Thread-4is locking
Thread-4is locking
Thread-4is locking
Thread-4is locking
Thread-1is locking
Thread-1is locking
Thread-1is locking
Thread-1is locking
Thread-1is locking
Thread-2is locking
Thread-2is locking
Thread-2is locking
Thread-2is locking
Thread-2is locking
Thread-0is locking
Thread-0is locking
Thread-0is locking
Thread-0is locking
Thread-3is locking
而我们的非公平锁出现连续获取锁的情况却非常多。而出现这种情况的原因会在后文源码分析中介绍到。
三、主要类与方法分析
3.1 Sync
在看源码之前我们先略微介绍下该类,这个类是继承于AQS,而Sync还有两个子类,分别是公平锁和非公平锁的两种不同的实现。
abstract static class Sync extends AbstractQueuedSynchronizer {
//具体实现由非公平和公平进行实现
abstract void lock();
//非公平尝试获取
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取AQS锁资源的数量
int c = getState();
//如果为0表示没有线程获取资源
if (c == 0) {
//利用CAS将state为0修改为当前资源数,并将当前的线程记录下来
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果state>0且当前线程和记录的线程是同一个,则重入处理(这里就是重入锁的相应实现)
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() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
//获取持有该锁的线程
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);
}
}
nonfairTryAcquire在后文会看到非公平锁对其的调用,而内部实现的处理正是我们可重入锁的具体处理方案。
3.2 FairSync
接下来是我们的公平锁的类,他实现了其父类Sync的lock方法。也同时对上一篇文章AQS的tryAcquire进行了重写
static final class FairSync extends Sync {
//获取锁,内部主要的实现由AQS的acquire实现
final void lock() {
//表示会获取一个锁资源,其中一部门就是由FairSync类下的tryAcquire实现具体实现可以看下上一篇文章AQS的acquire方法
acquire(1);
}
//对基类AQS的重写,表示获取资源
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//状态为0表示没有线程获取锁
if (c == 0) {
//当前节点是否有前置节点
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;
}
}
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
由于lock的主要逻辑在AQS的acquire中,而tryAcquire方法的重写,展示了Reetrantlock公平锁在获取资源与其它同步锁不同的特性。通过调用的acquire表示其是一个独占锁,而通过判断当前线程与记录线程的比较实现的是Reetrantlock可重入性。而最后判断当前节点是否有前置节点来实现了我们的公平性。
3.3 NonfairSync
static final class NonfairSync extends Sync {
final void lock() {
//如果能获取锁直接获取。
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//获取不了通过nonfairTryAcquire进行锁资源获取
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
//sync类中有实现
return nonfairTryAcquire(acquires);
}
}
公平锁和非公平锁的区别:
首先公平锁获取直接调用acquire方法,而该方法不是直接获取锁,而是调用的tryAcquire方法,而公平锁的tryAcquire首先获取state状态吐过没有线程占用(state=0),会判断在AQS的等待队列中的该节点的前置节点是否为其他同样等待的节点,如果有就说明在公平性上其他节点已经排好队了,那么会让出获取资源的权利。
而非公平锁,一上来就直接判断state状态,完全是一种抢占的机制。如果抢占不了在调用acquire方法,而非公平的acquire中的tryAcquire同样不会像公平锁的tryAcquire方法判断节点的前置节点。
3.4构造函数
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可以指定ReentrantLock的公平性
3.5 lock方法
public void lock() {
sync.lock();
}
直接调用公平锁或者非公平锁的lock
3.6 tryLock
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
尝试获取锁,不会发生阻塞,成功或失败直接返回。
3.7 unlock
public void unlock() {
sync.release(1);
}
释放锁直接调用AQS的release释放。