一,重入锁定义
重入锁,顾名思义,就是支持重新进入的锁,表示这个锁能够支持一个线程对资源重复加锁。如果一个线程已经拿到了锁,那么他需要再次获取锁的时候不会被该锁阻塞。
举个例子,
public synchronized void test(){
do something...
test2();
}
public synchronized void test2(){
do something;
}
test和test2都是同步方法,在执行之前都需要上锁,执行test获取到了锁,test里面调用test2,如果是重入锁,锁识别到获取锁的线程就是当前线程,那么直接进入。如果锁不支持重入,那么显然执行会阻塞在test2()这个位置。
当然,synchronized和ReentrantLock都是重入锁,均支持重新进入。
二,重入锁和非重入锁实现上的区别
上面算是理论,那么具体实现上如何支撑锁的重入性呢?
在队列同步器章节给出了Mutex锁的实现,这个Mutex就是一个非重入锁。
下面是他的代码逻辑,
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
class Mutex implements Lock {
// 静态内部类,自定义同步器
private static class Sync extends AbstractQueuedSynchronizer {
// 是否处于占用状态
protected boolean isHeldExclusively() {
return getState() == 1;
}
// 当状态为0的时候获取锁
public boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 释放锁,将状态设置为0
protected boolean tryRelease(int releases) {
if (getState() == 0) throw new
IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// 返回一个Condition,每个condition都包含了一个condition队列
Condition newCondition() {
return new ConditionObject();
}
}
// 仅需要将操作代理到Sync上即可
private final Sync sync = new Sync();
public void lock() {
sync.acquire(1);
}
public boolean tryLock() {
return sync.tryAcquire(1);
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
public boolean isLocked() {
return sync.isHeldExclusively();
}
public boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
lock方法实际上是去调用同步器的模板方法acquire(int args),而acquire方法又会调用重写的tryAcquire(int args)方法。在Mutex重写的tryAcquire逻辑中,直接尝试用CAS获取锁,成功返回true,失败返回false。
它并没有一个判断同步状态的逻辑,假设当前线程已经是持有锁的线程,重入的时候CAS失败线程就阻塞在了这里,所以Mutex不支持锁的重入。
对比ReentrantLock的逻辑,
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)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
而ReentrantLock的实现,是在上面基础上增加了再次获取同步状态的处理逻辑,即通过判断当前线程是否为持有锁的线程来决定获取操作是否成功,如果不是再去CAS,如果是直接将同步状态增加,相当于上锁次数+1。
既然多次上锁,那么在解锁的时候肯定也需要多次解锁,所以在tryRelease的实现上也会和Mutex不同,
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;
}
假设已经上了n次锁(也就是锁重入了n次),那么前(n-1)次tryRelease必须返回false,只有n次解锁,同步状态完全释放了,才能说我这个锁释放成功了。
而在Mutex的tryRelease逻辑里,只需要设置同步状态是0,直接释放锁即可。
三,synchronized和ReentrantLock
这两个都支持锁的重入,只不过synchronized是隐式的获取/释放锁,隐式地重进入。而ReentrantLock的lock()和unlock()都是我们手写的,在调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。
均参考整理自《Java 并发编程的艺术》
标签:重入,return,ReentrantLock,sync,boolean,线程,public From: https://www.cnblogs.com/chengyiyuki/p/18099445