读写锁- ReentrantReadWriteLock
ReentrantLock 不管公平锁还是非公平锁都是独占锁,不管将要进行的业务是读操作还是写操作都必须获得锁
ReentrantReadWriteLock 就是读写分离的锁,多个线程如果都是读操作可以同时获取到锁
本章只分析 ReentrantReadWriteLock,AQS 从入门到精通
类结构
// ReadWriteLock 接口提供了两个方法 readLock() 和 writeLock()
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {
}
类成员
类属性
类成员都是内部类,没啥好说的,还有 Unsafe 类的引用,就不贴出来了
private final ReentrantReadWriteLock.ReadLock readerLock;
private final ReentrantReadWriteLock.WriteLock writerLock;
final Sync sync;
内部类
abstract static class Sync extends AbstractQueuedSynchronizer {}
// 非公平锁
static final class NonfairSync extends Sync {
final boolean writerShouldBlock() { return false; }
final boolean readerShouldBlock() { return apparentlyFirstQueuedIsExclusive(); }
}
// 公平锁
static final class FairSync extends Sync {
final boolean writerShouldBlock() { return hasQueuedPredecessors(); }
final boolean readerShouldBlock() { return hasQueuedPredecessors(); }
}
// 读锁
public static class ReadLock implements Lock, java.io.Serializable {
private final Sync sync;
// 获取锁,获取不到就入队
public void lock() { sync.acquireShared(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); }
// 读锁不支持条件变量。条件变量的设计遵循 wait()、notify(),所以只能独占模式才能使用
public Condition newCondition() { throw new UnsupportedOperationException(); }
}
// 写锁
public static class WriteLock implements Lock, java.io.Serializable {
private final Sync sync;
// 获取锁,获取不到就入队
public void lock() { sync.acquire(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(); }
}
原理
加写锁
// java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock#lock
public void lock() {
sync.acquire(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 获取锁
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 获取锁失败,线程入队并挂起,AQS 提供
selfInterrupt();
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquire
protected boolean tryAcquire(int arg) { // 模板方法
throw new UnsupportedOperationException();
}
// java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquire
protected final boolean tryAcquire(int acquires) { // 获取锁原理
Thread current = Thread.currentThread();
int c = getState(); // 锁状态(低16位表示写锁,高16位表示读锁)
int w = exclusiveCount(c); // 写锁状态
if (c != 0) { // state 不等于0(两种情况:1 可能写锁被别的线程持有;2 可能读锁被别的线程持有)
// w==0表示写锁未被持有,说明读锁就已经被持有了,这里要获取写锁,因为读写互斥所以获取失败(除非当前线程获取的读锁)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 走到这里说明当前线程就是持有读锁的线程,可以获取写锁,但不能超过最大值 65535(16位最大值)
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
setState(c + acquires); // 修改 state
return true;
}
// 如果 c=0,说明读锁和写锁都还在,可以获取(虽然资源空闲但不一定就一定获取)
if (writerShouldBlock() || // 是否需要阻塞(公平锁:如果队里有其他节点,当前线程入队;非公平:尝试插队)
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
加读锁
// java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock#lock
public void lock() {
sync.acquireShared(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireShared
public final void acquireShared(int arg) {
// tryAcquireShared 方法根据返回值获取锁(可能返回 -1,0,1,对于读写锁只返回 1 和 -1)
if (tryAcquireShared(arg) < 0) // -1 表示获取锁失败,1 表示获取锁成功
doAcquireShared(arg); // 如果获取锁失败走这里,入队和阻塞线程,AQS 提供
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquireShared
protected int tryAcquireShared(int arg) { // 模板方法
throw new UnsupportedOperationException();
}
// java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquireShared
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState(); // 资源状态
if (exclusiveCount(c) != 0 && // 判断写锁是否被某个线程持有
getExclusiveOwnerThread() != current) // 持有写锁的线程是不是当前线程
return -1; // 写锁已被别的线程持有,不能获取读锁,返回 -1,获取锁失败(读写互斥)
// 读锁获取成功后的逻辑(走到这里说明写锁未被获取或获取写锁的线程就是自己,这时能获取读锁)
int r = sharedCount(c); // 读锁状态(多个线程可以获取,也能单个线程重入)
if (!readerShouldBlock() && // 是否需要阻塞(队列中第二个节点是否等待写锁,如果是就要阻塞,不然就不阻塞)
r < MAX_COUNT && // 不阻塞,也要验证读锁状态不能大于 65535
compareAndSetState(c, c + SHARED_UNIT)) { // 修改 state
if (r == 0) { // 读锁是0
firstReader = current;
firstReaderHoldCount = 1; // 重入次数为1
} else if (firstReader == current) { // 读锁不是0,但是持有读锁的线程就是当前线程
firstReaderHoldCount++; // 重入次数++
} 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 class Test {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(() -> {
resource.read();
}, "线程A").start();
new Thread(() -> {
resource.read();
}, "线程B").start();
}
}
class Resource{
private final ReentrantReadWriteLock rrw = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = rrw.readLock();
private final ReentrantReadWriteLock.WriteLock writeLock = rrw.writeLock();
public void read(){
readLock.lock();
System.out.println("获取读锁");
try {
System.out.println("读取数据....");
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
System.out.println("读取完成,释放读锁");
readLock.unlock();
}
}
}
读写分离
public class Test {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(() -> {
resource.read();
}, "线程A").start();
// 休眠 100 毫秒
try {
TimeUnit.MILLISECONDS.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 获取写锁,要等读锁释放才能获取
new Thread(() -> {
resource.write();
}, "线程B").start();
}
}
class Resource{
private final ReentrantReadWriteLock rrw = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = rrw.readLock();
private final ReentrantReadWriteLock.WriteLock writeLock = rrw.writeLock();
public void read(){
readLock.lock();
System.out.println("获取读锁");
try {
System.out.println("读取数据....");
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
System.out.println("读取完成,释放读锁");
readLock.unlock();
}
}
public void write(){
writeLock.lock();
System.out.println("获取写锁");
try {
System.out.println("写入数据....");
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
System.out.println("写入完成,释放写锁");
writeLock.unlock();
}
}
}

浙公网安备 33010602011771号