先抛问题,java Lock 有了 ReentrantLock,为什么还需要 ReentrantReadWriteLock ?
- ReentrantLock 可以解决 Lock 进行同步控制的需求,但在多个线程只需读数据的时候,ReentrantLock 也让线程进行排队控制,影响性能。ReentrantReadWriteLock 应运而生。
- ReentrantReadWriteLock 维护了一个 读写锁内部类ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.WriteLock,可以区分线程是读取还是写入操作
ReentrantReadWriteLock 详解
- ReentrantReadWriteLock 基本用法:读读共享,读写互斥,写写互斥
public class ReadWriteLockTest {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
ReadLock readLock = lock.readLock();
WriteLock writeLock = lock.writeLock();
public void read(){
readLock.lock();
try {
System.out.println("线程"+Thread.currentThread().getName()+"进入。。。");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"退出。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
readLock.unlock();
}
}
public void write(){
writeLock.lock();
try {
System.out.println("线程"+Thread.currentThread().getName()+"进入。。。");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"退出。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
writeLock.unlock();
}
}
public static void main(String[] args) {
final ReadWriteLockTest wr = new ReadWriteLockTest();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
wr.read();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
wr.read();
}
}, "t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
wr.write();
}
}, "t3");
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
wr.write();
}
}, "t4");
t1.start();
// t2.start();
t3.start();
//t4.start();
}
}
ReentrantReadWriteLock 源码
-
ReentrantReadWriteLock 也是 AQS 的实现类,即使用 int state = 0 来记录锁状态。
-
在一个整型变量上维护多种状态,就一定需要“按位切割使用”这个变量,读写锁将变量切分成了两个部分,高16位表示读,低16位表示写,划分方式如下图所示
-
再看ReentrantReadWriteLock 的静态变量
private final ReentrantReadWriteLock.ReadLock readerLock;
private final ReentrantReadWriteLock.WriteLock writerLock;
abstract static class Sync extends AbstractQueuedSynchronizer {
// 定义切割位数
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT= (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
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; }
}
- readLock.lock(); 底层调用 AQS 的 acquireShared
readLock.lock(); ==> sync.acquireShared(1);
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// 老规矩,AQS 定义天上的理念,子类需要落地的实现
protected int tryAcquireShared(int arg) {
thrownew UnsupportedOperationException();
}
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);
/* readerShouldBlock 读锁是否应该被阻塞 &&
* r < MAX_COUNT 有没有超过最大位数
* compareAndSetState(c, c + SHARED_UNIT) CAS设置 读锁位置(2^16 开始)
*/
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);
}
// 尝试共享失败后,doAcquireShared 添加一个Node.SHARED 进同步队列
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) {
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);
}
}
- 再来看 解锁unlock
readLock.unlock(); ==> sync.releaseShared(1);
public final boolean releaseShared(int arg) {
// 接触共享 count
if (tryReleaseShared(arg)) {
// 将node 从同步队列移除
doReleaseShared();
return true;
}
return false;
}
- 读锁跟 ReentrantLock 一致,都是加入同步队列。可以看从 ReentrantLock 的实现看懂AQS的原理
- 总结:读写锁就是为了解决读锁共享问题,即建立了一个读读共享标志,选择性的放过读线程的阻塞。
锁降级
- 锁降级指的是写锁降级成为读锁的过程。指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。
- 注意,如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级。
- 锁降级的示例代码
public class ReadWriteLockTest1 {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
ReadLock readLock = lock.readLock();
WriteLock writeLock = lock.writeLock();
public static void main(String[] args) {
final ReadWriteLockTest1 wr = new ReadWriteLockTest1();
new Thread(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
wr.readLock.lock();
try {
System.out.println("==============");
} catch (Exception e) {
e.printStackTrace();
} finally {
wr.readLock.unlock();
}
}).start();
wr.writeLock.lock();
try {
System.out.println("我现在是写锁");
}finally {
// 先获取读锁
wr.readLock.lock();
try {
System.out.println("----------睡眠之前--------");
wr.writeLock.unlock();
System.out.println("----------锁降级变为读锁之后,就可以共享锁--------");
Thread.sleep(10000);
System.out.println("----------睡眠之后--------");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
wr.readLock.unlock();
}
}
}
}