ReentrantLock也叫重入锁,可以对同一线程资源进行重复加锁。通过lock()方法可以显式的加锁,并且再次调用lock(),不会出现阻塞的情况
Sync子类提供锁的基本实现机制
-
非公平锁的获取
- 获取独占锁后,增加状态码
//加锁
final void lock() {
if (compareAndSetState(0, 1))//获取到锁,增加锁状态码为1
setExclusiveOwnerThread(Thread.currentThread());
else
/**
* 调用AQS中的acquire方法,加入到同步队列中
* 循环调用tryAcquire方法,当前节点的前驱是头节点时,设置当前节点为头节点
*/
acquire(1);
}
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;
}
-
释放锁操作
- 释放锁后更新减少状态码,同步状态为0时,表示锁释放成功
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//状态为0时,独占线程置为空,释放线程
free = true;
setExclusiveOwnerThread(null);
}
setState(c);//更新状态
return free;
}
公平锁FairSync
-
公平锁的获取锁方法
- 同步队列的头节点进行获取锁,如果成功,则返回true,如果未获取成功,返回false
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
/**
* 1.当前线程节点为头节点,并通过CAS成功将state设置成acuires值
* 返回true
*/
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
/**
* 当前线程和独占线程一致时
* 1.更改状态;
* 2.返回获取锁成功
*/
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; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
非公平锁NonfairSync
- 直接调用SYN的nofairTryAcquire方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
公平锁和非公平锁创建
-
通过传入true或false,初始化创建公平锁或非公平锁
-
默认是不公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public ReentrantLock() {
sync = new NonfairSync();
}
公平锁和非公平锁的优缺点
-
公平锁:
优点:保证了按照锁的获取的时间长短,时间最长的先获取到锁。会获取当前节点所在等待队列的位置,必须按照位置执行。
缺点:
-
因为要判断前驱,且严格按照同步队列顺序,因此效率更低
-
也因为顺序执行,所以多次获取锁操作,会造成上下文切换冗余,切换次数更多
-
-
非公平锁:
优点: 因为只需要判断状态码,因此,同一个锁再次获取到的概率比较大,所以锁切换次数少,效率比较高。保证有更大的吞吐量。
缺点: 同一个锁的多次调用,容易造成,单个锁调用频繁,线程出现“饥饿”状态;
(图片摘自《Java并发编程艺术》)