ReentrantReadWriteLock
一、ReentrantReadWriteLock是什么?
ReentrantReadWriteLock采用读写分离的策略,允许多个线程可以同时获取读锁。
可以重入的读写锁,解决读多写少的情况,避免多线程下共享变量的争抢情况,提升执行效率。
规则: 读读兼容、 读写互斥
同一个线程可以拥有 writeLock 与 readLock (但必须先获取 writeLock 再获取 readLock, 反过来进行获取会导致死锁)
特点:
- 支持公平和非公平的获取锁的方式;
- 支持可重入,读线程在获取了读锁后还可以获取读锁;写线程在获取了写锁之后既可以再次获取写锁又可以获取读锁;
- 允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读锁,最后释放写锁。但是,从读取锁升级为写入锁是不允许的;
- 读取锁和写入锁都支持锁获取期间的中断;
- Condition支持,仅写入锁提供了Condition实现,读取锁不支持Condition,readLock().newCondition()会抛出异常;
二、类结构组织&&构造函数
类结构组织
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
构造函数
默认为非公平锁,初始化ReadLock和WriteLock。
非公平锁执行效率高,可以减少上下文切换成本。
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
二、如何使用
private static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
for (int i = 0; i < 3; i++) {
threadPool.execute(() -> {
try {
readWriteLock.writeLock().lockInterruptibly();
System.out.println("写入数据" + test);
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
});
}
for (int i = 0; i < 20; i++) {
threadPool.execute(() -> {
try {
readWriteLock.readLock().lockInterruptibly();
System.out.println("读取数据" + test);
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
});
}
三、核心概念源码认证
ReentrantReadWriteLock类结构说明
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
private final ReentrantReadWriteLock.ReadLock readerLock;//读锁
private final ReentrantReadWriteLock.WriteLock writerLock;//写锁
final Sync sync;//同步器
abstract static class Sync extends AbstractQueuedSynchronizer {}//同步器
//内部类 公平 和 非公平实现
static final class FairSync extends Sync {}
static final class NonfairSync extends Sync {}
//内部类 读锁和写锁
public static class WriteLock implements Lock, java.io.Serializable {}
public static class ReadLock implements Lock, java.io.Serializable {}
}
核心同步器Sync
提供了公平和非公平锁的模板方法,内部有一个32位的state变量,我们这里切分高16位,低16位表示读锁和写锁的数量。
abstract static class Sync extends AbstractQueuedSynchronizer {
static final int SHARED_SHIFT = 16;//位数切割器
static final int SHARED_UNIT = (1 << SHARED_SHIFT);//高16位代表读锁
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;//最大锁数量
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;//低16位的锁数量&计算
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }//获取目前持有读锁的线程数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }//获取目前持有写锁的线程数量
//高16位是读锁获取次数,这里可以获取当前线程重入锁的次数
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
//继承ThreadLocal 初始化HoldCounter
static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
private transient ThreadLocalHoldCounter readHolds;//新建ThreadLocalHoldCounter
private transient HoldCounter cachedHoldCounter;//缓存最后一个线程获取读锁数量
private transient Thread firstReader = null;//保存第一个获取该读锁的线程
private transient int firstReaderHoldCount;//保存第一个获取该锁读锁的线程,获取读锁的数量
Sync() {
readHolds = new ThreadLocalHoldCounter();//初始化ThreadLocalHoldCounter
//使用volatile 保证可见性 readHolds
setState(getState()); // ensures visibility of readHolds
}
}
公平锁和非公平锁
独占锁
公平锁非公平锁
writerShouldBlock() 这里查看队列是否有等待线程 公平锁和非公平的差距在这里
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();获取当前线程
int c = getState();获取State
int w = exclusiveCount(c);计算独占锁的数量
if (c != 0) {State锁数量不为0
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())独占锁的数量是0 或 当前线程不是独占线程
return false; 返回false 进入等待队列
if (w + exclusiveCount(acquires) > MAX_COUNT)计算独占线程是否大于最大线程数量限制
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);设置写锁数量
return true;
}
if (writerShouldBlock() ||查看队列是否有等待线程 公平锁和非公平的差距在这里
!compareAndSetState(c, c + acquires))或则CAS设置抢锁
return false; 返回false 进入等待队列
setExclusiveOwnerThread(current);获取到锁 设置自己独占
return true;
}
共享锁
公平锁非公平锁*
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();获取当前线程
int c = getState();获取State
if (exclusiveCount(c) != 0 && 独占线程是否不为0,说明有人占用写锁
getExclusiveOwnerThread() != current) 判断是否是写锁 重入,即先获取写锁,可以在此获取读锁
return -1;
int r = sharedCount(c);计算写锁数量
if (!readerShouldBlock() && 公平锁和非公平锁判断 是否阻塞等待
r < MAX_COUNT &&最大锁数量校验
compareAndSetState(c, c + SHARED_UNIT)) {cas尝试占有锁
if (r == 0) {如果之前写锁数量是0 说明当前第一个写锁
firstReader = current;记录第一个写锁
firstReaderHoldCount = 1;
} else if (firstReader == current) {如果同一个写锁
firstReaderHoldCount++; firstReaderHoldCount+1
} else {
HoldCounter rh = cachedHoldCounter;记录最后一个读锁
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
构造方法
public interface ReadWriteLock {
Lock readLock();//获取读锁
Lock writeLock();//获取写锁
}
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
private final ReentrantReadWriteLock.ReadLock readerLock;//读锁
private final ReentrantReadWriteLock.WriteLock writerLock;//写锁
final Sync sync;//同步器
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
}
核心内部类
Sync
AbstractQueuedSynchronizer 中有一个32位的state变量,我们这里切分高16位,低16位。
abstract static class Sync extends AbstractQueuedSynchronizer {
static final int SHARED_SHIFT = 16;//高16位表示读锁
static final int SHARED_UNIT = (1 << SHARED_SHIFT);//对高16位+1
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;//最大锁数量
//用于获取低16位的值0000 0000 0000 0000 1111 1111 1111 1111
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }//获取目前持有读锁的线程数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }//获取目前持有写锁的线程数量
//高16位是读锁获取次数,这里可以获取当前线程重入锁的次数
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
//继承ThreadLocal 初始化HoldCounter
static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
private transient ThreadLocalHoldCounter readHolds;//新建ThreadLocalHoldCounter
private transient HoldCounter cachedHoldCounter;//缓存最后一个线程获取读锁数量
private transient Thread firstReader = null;//保存第一个获取该锁读锁的线程
private transient int firstReaderHoldCount;//保存第一个获取该锁读锁的线程,获取读锁的数量
Sync() {
readHolds = new ThreadLocalHoldCounter();//初始化ThreadLocalHoldCounter
//使用volatile 保证可见性
setState(getState()); // ensures visibility of readHolds
}
}
WriteLock
获取写锁
lock —》 acquire —》 tryAcquire
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
获取当前线程
Thread current = Thread.currentThread();
获取State
int c = getState();
计算写锁数量
int w = exclusiveCount(c);
状态值有效
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
写锁数量为0 或则 当前线程不是线程持有者
if (w == 0 || current != getExclusiveOwnerThread())
return false;
计算写锁数量是否大于最大最大锁数量
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);设置写锁数量 写回State
return true;
}
没有读锁 也没有写锁
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))cas抢写锁
return false;
setExclusiveOwnerThread(current);成功
return true;
}
ReadLock
获取写锁
lock —》 acquireShared—》 tryAcquireShared
public void lock() {
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
获取当前线程
Thread current = Thread.currentThread();
获取State
int c = getState();
获取写锁是否为0 并且 锁独占线程是不是当前线程
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current) 重入校验
return -1;
获取写锁数量 c >>> SHARED_SHIFT(16)
int r = sharedCount(c);
if (!readerShouldBlock() && 判断是否是公平锁
r < MAX_COUNT && 最大数量校验
compareAndSetState(c, c + SHARED_UNIT)) { cas加锁
if (r == 0) { 读锁是0
firstReader = current; 设置第一个读锁
firstReaderHoldCount = 1; 设置第一个读锁,获取锁次数
} else if (firstReader == current) {
firstReaderHoldCount++; 设置第一个读锁,获取锁次数
} else {
HoldCounter rh = cachedHoldCounter; 获取缓存最后一个线程获取读锁
if (rh == null || rh.tid != getThreadId(current))判断为空 和 线程是否相等
cachedHoldCounter = rh = readHolds.get();获取当前线程的cachedHoldCounter
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;设置
}
return 1;
}
return fullTryAcquireShared(current);
}