ReentrantLock
-
Reentrantlock 是并发包中一个可重入的锁,是基于AQS(AbstractQueuedSynchronized)实现的,它有公平锁和不公平锁两种实现方式。
-
Reentranlock 中有一个内部抽象类 Sync 继承自 AbstractQueuedSynchronized ,主要是它来实现锁的功能, Sync 在 ReentrantLock 中有两种实现类:NonfairSync、FairSync,正好对应了ReentrantLock的非公平锁、公平锁两大类型。Reentranlock 默认实现为非公平锁,在高竞争的条件下有更好的性能。
与synchronized区别
-
synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
-
synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
-
synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。
-
jdk8之前,Synchronized是重量级锁,ReentrantLock相比较比较轻量级。
定义
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
private final Sync sync;//同步器
//抽象的aqs实现
abstract static class Sync extends AbstractQueuedSynchronizer {
//*****省略********
}
//非公平
static final class NonfairSync extends Sync {
//*****省略********
}
//公平
static final class FairSync extends Sync {
//*****省略********
}
//构造方法
public ReentrantLock() {
sync = new NonfairSync();//默认非公平锁
}
public ReentrantLock(boolean fair) {//设置公平非公平
sync = fair ? new FairSync() : new NonfairSync();
}
//调用sync的lock方法。其在NonfairSync和FairSync中根据是否公平实现
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//尝试获取锁
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
//解锁
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
}
Sync
ReentrantLock基于AQS实现。对于排他锁来说,我们一般只需要是实现AQS的两个方法
- tryAcquire
- tryRelease
当需要获得锁时,调用AQS的acquire即可。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
//抽象函数,根据公平非公平交给子类实现
abstract void lock();
//非公平尝试获取锁,之所以写在这,是因为在公平情况下,当TryLock的时候,即使是设置的公平但仍然会调用这个非公平的方法,可以理解为是TryLock的机制吧
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {//与公平锁的区别,不需要判断aqs队列的情况,直接cas
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;
}
//****省略******
}
非公锁实现
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))//非公平情况下会直接通过cas尝试获取锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
公平锁实现
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);//acquire被定义在aqs中,其首先调用了下面的tryAcquire方法尝试获取锁
}
//尝试获取锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();//获取state
if (c == 0) {//自由状态
/**
因为是公平锁,首先检查之前是否需要排队
1.hasQueuedPredecessors检查是否已经有线程正在等待资源释放
2.如果没有其他线程正在等锁,则利用cas设置state获取锁
*/
if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);//设置当前线程为获取到锁的线程
return true;
}
/**hasQueuedPredecessors源码,定义在父类AQS中了
AQS中维护了一个双向链表,存储了所有的尝试获取锁的线程
public final boolean hasQueuedPredecessors() {
Node t = tail; //队列尾部
Node h = head;//队列头部
Node s;
//如果除了本线程之外,在队列中只有一个线程则返回true,否则false
//意思是,如果在队列中存在线程正在等,则返回true
//这个代码很nb了,考虑了各种情况
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
1.当aqs的队列未初始化时,head和tail都是null,返回false,说明此时没有线程排队,可以尝试获取
2. s.thread != Thread.currentThread()判断的是当已经入队的线程在进行自旋时调用了tryAquire,s.thread != Thread.currentThread()不成立,所以返回false,进一步执行cas尝试获取锁
}
*/
}
//如果state不等于0,说明已经有线程获取到锁,判断当前线程是否是持有锁的那个线程
//即 可重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);//重入锁 给state加1,表示重入的次数
return true;
}
return false;//尝试获取锁失败
}
}