引言:相较于互斥锁,读写锁在保证数据的安全性的基础上提高了性能,不过不能保证数据的实时可见性,这需要锁降级来规避。
一、简单使用读写锁
作者简单写了一个读写锁的示例来演示读取小于等于特定数值的数并打印的场景。
/**
* @Author Mark_Yao
*/
public class CountManage {
private int count = 0;
private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static final Lock readLock = readWriteLock.readLock();
private static final Lock writeLock = readWriteLock.writeLock();
public void read() {
readLock.lock();
try {
if (count <= 10) {
System.out.println(count);
}
} finally {
readLock.unlock();
}
}
public void write() {
writeLock.lock();
try {
if (count <= 20) {
count += 1;
}
} finally {
writeLock.unlock();
}
}
}
/**
* @Author Mark_Yao
*/
public class Main {
private static final CountManage countManage = new CountManage();
public static void main(String[] args) {
for (int i = 0; i < 4; i++) {
new Thread(new ReadRunnable()).start();
}
for (int i = 0; i < 2; i++) {
new Thread(new WriteRunnable()).start();
}
}
static class ReadRunnable implements Runnable {
@Override
public void run() {
// 仅因为演示使用死循环
for (;;) {
try {
Thread.sleep(100);
countManage.read();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
static class WriteRunnable implements Runnable {
@Override
public void run() {
// 仅因为演示使用死循环
for (;;) {
try {
Thread.sleep(100);
countManage.write();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
二、AQS共享模式
/**
* 共享模式下的资源请求
*/
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
// tryAcquireShared函数返回的标志位大于0就可以获取资源
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
/**
* 设置头节点并传播共享状态
*/
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 有两种情况需要继续传播共享状态,一个是后续节点为空,另一个是后续节点为共享状态
if (s == null || s.isShared())
doReleaseShared();
}
}
/**
* 共享模式下释放资源
*/
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
// 避免多次唤醒同一个节点,浪费资源
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
unparkSuccessor(h);
}
// 安全设置头节点为PROPAGATE状态,避免设置已经取消的节点状态
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
// 旧的头节点不等于当前头节点,说明传播还未结束,需要继续循环协助传播
if (h == head)
break;
}
}
三、ReentrantReadWriteLock核心函数
1.写锁的获取
/**
* 写锁获取锁的方式
*/
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 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);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
2.读锁的获取
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
// 存在写锁并且写锁不是被当前线程持有,不存在锁降级的场景
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 读状态
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 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();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}