1.Lock
本身是一个接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
lock
获取锁,如果获取不到就阻塞lockInterruptibly
获取锁,如果获取不到就阻塞线程。并且检查线程中断情况。如果有中断,在获取锁后,抛出异常InterruptedException
,然后回重置中断标志。tryLock
尝试获取锁,立马返回结果。tryLock(long time, TimeUnit unit)
尝试获取锁,获取到就返回true,如果获取不到,就阻塞线程,并有设置有超时时间。超时后,返回获取结果。也会检查线程中断情况。unlock
释放锁,释放成功,唤醒后继节点线程。newCondition
创建一个绑定在当前Lock对象上的Condition对象,这说明Condition对象和Lock对象是对应的,一个Lock对象可以创建多个Condition对象,它们是一个对多的关系。
2.ReentrantLock
3.Condition
4.ReentrantReadWriteLock
内部主要有五个内部类, Sync、FairSync、NonfairSync、ReadLock、WriteLock。
ReadLock、WriteLock 分别继承了 Lock 接口。
ReadLock 使用的是共享模式,WriteLock 使用的独占模式。在 AQS 中 state 代表了锁被线程持有的状态。那么一个字段如何来同时代表两种锁模式的状态呢?答案是同过位运算,在 state 的 32 个位数中,高 16 位表示共享模式读的数量,低 16 位表示独占模式写的数量。
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {
private static final long serialVersionUID = -6992448646407690164L;
/** Inner class providing readlock */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** Inner class providing writelock */
private final ReentrantReadWriteLock.WriteLock writerLock;
/** Performs all synchronization mechanics */
final Sync sync;
public ReentrantReadWriteLock() {
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
//创建锁实际的操作类型
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
}
Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
/*
* Read vs write count extraction constants and functions.
* Lock state is logically divided into two unsigned shorts:
* The lower one representing the exclusive (writer) lock hold count,
* and the upper the shared (reader) hold count.
*/
//通过这几个字段和 state 进行位运算,来计算出 共享模式、独占模式线程持有锁的状态。
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);//00000000 00000001 00000000 00000000
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;//00000000 00000000 11111111 11111111
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;//00000000 00000000 11111111 11111111
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }//共享线程的数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }//独占线程的数量
}
ReadLock
public static class ReadLock implements Lock, java.io.Serializable {
private final Sync sync;
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() {
sync.acquireShared(1);
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean tryLock() {
return sync.tryReadLock();
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.releaseShared(1);
}
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
WriteLock
public static class WriteLock implements Lock, java.io.Serializable {
private final Sync sync;
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() {
sync.acquire(1);
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock( ) {
return sync.tryWriteLock();
}
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();
}
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
public int getHoldCount() {
return sync.getWriteHoldCount();
}
}
可以看出读写锁的具体操作逻辑都是由 Sync 实现的。其它逻辑都在 AQS 的共享锁中有过分析。
写锁是一个独占锁,它在获取锁的逻辑和 ReentrantLock 的独占锁获取锁的逻辑不太一样。
tryAcquire
//ReentrantReadWriteLock.Sync.java
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
//这里也给了注释,如果 c!=0 并且 w=0,那么说明还有读的线程在持有锁
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//超过最大数,就抛出错误
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
//能够执行到这里,说明没有读线程持有锁,并且是当前线程持有了锁。就直接更新 state,而不用通过 CAS来更新。
setState(c + acquires);
return true;
}
//执行到这里,说明其它写线程持有锁,那么当前写线程就要争夺锁。
//writerShouldBlock 是由 Sync 的子类 FairSync 和 NonfairSync 来实现。它们两个的实现方式不同。
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;//争夺锁失败
//争夺锁成功,将当前的线程设置到独占线程的标识上。
setExclusiveOwnerThread(current);
return true;
}
和 ReentrantLock 不同的是,这里的独占锁获取锁的时候,由于共享锁的存在,对 state 的处理也要复杂一点。要先判断是否还有共享锁持有线程。再去争夺锁。
//ReentrantReadWriteLock.FairSync.java
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
//ReentrantReadWriteLock.NonfairSync.java
final boolean writerShouldBlock() {
return false; // writers can always barge
}
从上面也可以看出,公平锁、非公平锁的判断逻辑和 ReentrantLock 是一样的。
公平锁要先看当前队列是否还有等待的线程,如果有,自己就争夺锁失败,然后老老实实的去排队。
非公平锁则直接去争夺。
写饥饿
ReentrantReadWriteLock实现了读写锁,但它有一个小弊端,就是在“写”操作的时候,其它线程不能写也不能读。我们称这种现象为“写饥饿”
5.CountDownLatch
6.CyclicBarrier
7.Semaphore
8.StampedLock
StampedLock
类是在Java 8 才发布的,也是Doug Lea大神所写,有人号称它为锁的性能之王。它没有实现Lock接口和ReadWriteLock接口,但它其实是实现了“读写锁”的功能,并且性能比ReentrantReadWriteLock更高。StampedLock还把读锁分为了“乐观读锁”和“悲观读锁”两种。
前面提到了ReentrantReadWriteLock会发生“写饥饿”的现象,但StampedLock不会。它是怎么做到的呢?它的核心思想在于,在读的时候如果发生了写,应该通过重试的方式来获取新的值,而不应该阻塞写操作。这种模式也就是典型的无锁编程思想,和CAS自旋的思想一样。这种操作方式决定了StampedLock在读线程非常多而写线程非常少的场景下非常适用,同时还避免了写饥饿情况的发生。