Java 多线程学习之 ReentrantLock
Java 多线程学习之 ReentrantLock
一、什么是 ReentrantLock
ReentrantLock 中文译为‘可重入锁’,是 java.util.concurrent.locks 包下的一个类,实现了 Lock 接口,在多线程中用来保证线程安全。
使用 demo:
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
int i = 1;
lock.lock();
System.out.println("第" + i + "次获取锁,这个锁是:" + lock);
for (i = 2; i < 10; i++) {
try {
lock.lock();
System.out.println("第" + i + "次获取锁,这个锁是:" + lock);
} finally {
lock.unlock();
}
}
} finally {
lock.unlock();
}
}
}).start();
}
二、公平锁和非公平锁
ReentrantLock 有三个内部类:Sync、NonfairSync、FairSync。 Sync 继承了抽象类 AbstractQueuedSynchronizer(俗称 AQS),NonfairSync、FairSync 都继承了 Sync,分别采用了非公平锁和公平锁的策略去获取锁。
公平锁
公平锁:
优点:
缺点:
非公平锁
非公平锁:
优点:
缺点:
根据 ReentrantLock 的构造函数可知:ReentrantLock 默认采用非公平锁:
public ReentrantLock() {
sync = new NonfairSync();
}
三、ReentrantLock 和 AQS
1. ReentrantLock 和 AQS 的关系
ReentrantLock 使用 demo:
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
try {
reentrantLock.lock();
System.out.println("lock");
} finally {
reentrantLock.unlock();
}
}
点进 lock() 和 unlock() 方法,发现实际上是内部类 Sync 在调用 lock() 和 unlock() 方法,而 Sync 又是继承的 AbstractQueuedSynchronizer(AQS),所以 ReentrantLock 执行的方法实际上是通过 AQS 来实现的!
public void lock() {
sync.lock();
}
public void unlock() {
sync.release(1);
}
2. Sync、NonfairSync、FairSync 源码分析
Sync:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
//上锁
abstract void lock();
//非公平获取
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取 AQS 的 state 值
int c = getState();
if (c == 0) {
//如果 state 为 0 说明资源未被占用即当前线程获取资源成功
//用 CAS 更新 state
if (compareAndSetState(0, acquires)) {
//设置占用线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
//如果获取失败则把线程改为占用资源的线程
} else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // 正常情况下 state 不可能小于 0
throw new Error("Maximum lock count exceeded");
//更新 state 值
setState(nextc);
return true;
}
return false;
}
//释放资源
protected final boolean tryRelease(int releases) {
//获取当前 state
int c = getState() - releases;
//释放资源的前提是占据了资源,否则不正常,报错!
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//state 为 0 说明当前线程是最后一个获取资源的线程,释放后设置占用资源线程为空
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//更新 state
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();
}
//获取 state 值
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); // 重置未被锁的状态
}
}
NonfairSync:
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);
}
//尝试获取资源,实际是 Sync 中的“非公平获取”方法的结果
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
FairSync:
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
//上锁
final void lock() {
//直接加入到队列中,等待获取资源
acquire(1);
}
//尝试获取资源
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取 state
int c = getState();
if (c == 0) {
//等待队列中前面没有其他线程且能成功修改 state 值时才把自己设为占据资源线程
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
//设为资源拥有者
setExclusiveOwnerThread(current);
return true;
}
//如果当前线程已占据资源的话,不舍弃资源,仅修改 state 值
} else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//如果当前线程没有占据资源,且当前 state 值不为 0,则返回 false
return false;
}
}
分析:
ReentrantLock 的 lock :实际上是内部 NonfairSync 或 FairSync 调用的 lock() 方法,而两者的区别在于:NonfairSync(非公平锁)会先判断能否获取资源,获取不到再把线程加到等待队列中;FairSync(公平锁)会直接加到等待队列中,等轮到自己的时候才能获取到资源。
ReentrantLock 的 unlock:实际是内部类 Sync 的 release() 方法,而在 release() 中又调用了 tryRelease(arg) ,因为 tryRelease 方法是在内部类 Sync 中实现的,所以并不分“公平锁”和“非公平锁”,即 ReentrantLock 无论用公平锁还是非公平锁,它的解锁方法都是统一的。
public void unlock() {
sync.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;
}