ReentrantLock
ReentrantLock是可重入锁
分为公平锁和非公平锁。两者有什么区别?
NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
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);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
}
两者的区别
- 都会调用AQS提供的acquire()方法
- unFairLock(非公平锁)会先进行一次CAS操作看看能不能获取到状态
- FairLock和unFairLock分别自定义实现模板方法tryAcquire,公平锁会加多一个hasQueuedPredecessors方法的判断,也就是说公平锁会考虑先来后到的原则,如果前面有节点或者线程在排队那么他就放弃争夺。换成非公平锁的话,他会优先通过CAS进行争夺,忽略前面排队的节点或线程,抢夺成功的话那么就比早来的线程先获取到了锁。
- 后续操作一样,但仍然会涉及到tryAcquire的调用
- 总结:两者的加锁主要区别于第一次的尝试,尝试失败后都需要加入等待队列中,
然后不断轮训+dump直到获取到状态
//FairLock的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;
}
//unFairLock的tryAcquire
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
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;
}
ReentranReadWriteLock
读写锁,内部分别通过两个内部类ReadLock和WriteLock分别实现
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
final Lock readLock = rwl.readLock();
final Lock writeLock = rwl.writeLock();
读写锁原理
读写锁的状态依然是利用state控制,不同的是读锁的获取是共享式的(意味允许多个线程同时获取),而写锁的获取是独占式的
读锁的状态取的是state字段的高16位,而写锁的状态取的是低16位
读锁状态获取
写锁状态获取
锁降级
锁降级指的是写锁降为读锁的过程。以下过程中,在写锁释放前,先获取了读锁再释放写锁,这样做的目的是为了防止脏读,同时又可以提高线程访问共享变量的效率。
//伪代码
//获取写锁
writeLock.lock();
//修改数据
set a = 1;
//获取读锁
readLock.lock();
//释放写锁
writeLock.unlock();
//开始进行其他数据处理
StampedLock
相比于ReentrantReadWriteLock,StampedLock的读是乐观读,在读过程中运行有线程写入,当有线程写入时,读的线程能够发现。