基础
- 重入锁:一个线程对同一个锁资源可以重复获取(如synchronized关键字)
- ReentrantLock是可重入锁,并且支持获取锁时的公平与非公平选择。
- ReentrantLock默认为非公平锁,这是出于性能考虑(对于非公平锁,一个刚释放锁的线程很大概率可以再次获得该锁,而不用去排队,减少上下文切换次数)。而公平锁总是会让你到同步队列走一遭,即使你刚释放了锁,然后立马请求锁。即公平锁的实现是以线程切换为代价的。
基本使用
ReentrantLock lock = new ReentrantLock();//非公平锁
//ReentrantLock lock = new ReentrantLock(true);//公平锁
lock.lock();
try{
//to do
}finally {
lock.unlock();
}
ReentrantLock的基本骨架
ReentrantLock implements Lock{
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {......};
static final class NonfairSync extends Sync {};//非公平锁用这个同步器
static final class FairSync extends Sync {};//公平锁用这个同步器
}
public ReentrantLock(boolean fair) {//选择是公平锁还是非公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
public ReentrantLock() {//默认为非公平锁
sync = new NonfairSync();
}
源码角度分析其可重入性和公平性的实现
可重入特性实现(非公平锁为例)
锁的获取
如果当前同步状态已经被线程获取(state != 0),判断当前尝试获取同步状态的线程是否是已经获取同步状态的线程,如果是就代表当前线程重入获取锁,将同步状态的值增加(所以获取锁多少次,就应该释放锁多少次),返回成功。
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;
}
锁的释放
因为同步状态的获取可能获取了n次,所以应该只有最后一次释放之后才应该返回true(线程已释放锁)。
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;
}
公平性实现
用户层面而言:锁的获取严格遵守FIFO模式
源码角度来说:公平锁的tryAcquire与非公平锁不同之处在于---公平锁如果判断锁空闲,还需要去判断同步队列中有没有在排队的(非公平锁没有这一步),如果有的话就去同步队列中排队。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
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;
}