Java并发之ReentrantReadWriteLock
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java并发之ReentrantReadWriteLock,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含8287字,纯文字阅读大概需要12分钟。
内容图文
![Java并发之ReentrantReadWriteLock](/upload/InfoBanner/zyjiaocheng/623/6a4e752b220140f5bbe457dd28281392.jpg)
ReadWriteLock
ReadWriteLock是一个读写锁接口,所谓读写锁,是对访问资源共享锁和互斥锁,一般的重入性语义为如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读锁(锁降级);如果一个线程对资源加了读锁,其他线程可以继续加读锁。
ReadWriteLock接口中定义了两个方法:
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
ReentrantReadWriteLock源码分析
ReentrantReadWriteLock实现了ReadWriteLock接口和Serializable接口:
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
......
}
构造方法:
public ReentrantReadWriteLock() {
//调用有参数构造方法实现
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
//是否是公平锁
sync = fair ? new FairSync() : new NonfairSync();
//读锁
readerLock = new ReadLock(this);
//写锁
writerLock = new WriteLock(this);
}
默认情况下,ReentrantReadWriteLock为非公平锁。
ReentrantReadWriteLock中有五个内部类,分别是:ReadLock、WriteLock、FairSync、NonfairSync、Sync,其中Sync继承自AQS类,FairSync、NonfairSync继承自Sync类。
ReentrantReadWriteLock读写锁同步状态通过一个整型位来表示状态,高16位表是读状态,低16位表是写状态;
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; }
读锁
lock
我们先看下读锁,读锁的lock方法源码:
public void lock() {
//调用AQS类acquireShared方法
sync.acquireShared(1);
}
AQS类中acquireShared源码:
public final void acquireShared(int arg) {
//尝试加锁
if (tryAcquireShared(arg) < 0)
//加锁失败,阻塞住
doAcquireShared(arg);
}
在之前的文章中分析AQS类我们说过tryAcquireShared方法在AQS类中是无法直接调用的,Sync类中实现了tryAcquireShared方法,源码:
protected final int tryAcquireShared(int unused) {
//获取当前线程
Thread current = Thread.currentThread();
//获取线程同步状态
int c = getState();
//计算写锁是否为0并且当不为0时判断持锁线程是否为当前线程
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
//存在写锁,并且写锁持有者不为当前线程,加锁失败
return -1;
//没有线程持有写锁,获取持有读锁数
int r = sharedCount(c);
//readerShouldBlock():读锁是否需要等待,r < MAX_COUNT:持有线程小于最大数(65535),compareAndSetState(c, c + SHARED_UNIT):设置读取锁状态
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
//持有读锁数为0
if (r == 0) {
//获取读锁的第一个线程设置为当前线程
firstReader = current;
//firstReader持有读锁数量为1
firstReaderHoldCount = 1;
//持有读锁数不为0,且持有读锁线程为当前线程
} else if (firstReader == current) {
//将firstReader持有读锁数量加1
firstReaderHoldCount++;
//持有读锁数不为0,且持有线程不为当前线程
} else {
//获取HoldCounter
HoldCounter rh = cachedHoldCounter;
//HoldCounter为空,或者获取的HoldCounter不是当前线程的HoldCounter
if (rh == null || rh.tid != getThreadId(current))
//重新获取HoldCounter并设置rh和cachedHoldCounter
cachedHoldCounter = rh = readHolds.get();
//rh不为空,且rh的值为0
else if (rh.count == 0)
//设置线程自有的HoldCounter
readHolds.set(rh);
//持有读锁加1
rh.count++;
}
//成功加锁
return 1;
}
//自旋尝试加锁
return fullTryAcquireShared(current);
}
readerShouldBlock()在Sync中定义为抽象类,由子类FairSync或NonfairSync实现;
对于FairSync中readerShouldBlock方法:
final boolean readerShouldBlock() {
//调用AQS类hasQueuedPredecessors实现
return hasQueuedPredecessors();
}
//AQS类hasQueuedPredecessors方法
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
//判断队头线程是否为当前线程,是的话返回false,否则返回true
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
对于NonfairSync中的readerShouldBlock方法:
final boolean readerShouldBlock() {
//调用AQS类中apparentlyFirstQueuedIsExclusive实现
return apparentlyFirstQueuedIsExclusive();
}
//AQS类apparentlyFirstQueuedIsExclusive方法
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
//链表头不为空,当前运行线程不为空,运行线程为独占模式返回true,否则返回false
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
在Sync类中,还有两个内部类,分别是:HoldCounter、ThreadLocalHoldCounter,HoldCounter类有两个成员变量:count和tid,其中count是用来计数线程持有锁的数量而tid是通过一个线程的id来标志是哪个线程的HoldCounter,HoldCounter保存在每个线程的ThreadLocal中;ThreadLocalHoldCounter是ThreadLocal的子类用于维护每个线程自己的HoldCounter。
unLock
读锁中unLock源码:
public void unlock() {
//调用AQS类实现
sync.releaseShared(1);
}
//AQS类releaseShared方法
public final boolean releaseShared(int arg) {
//尝试释放锁,交由子类实现
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
//Sync类中tryReleaseShared方法
protected final boolean tryReleaseShared(int unused) {
//获取当前线程
Thread current = Thread.currentThread();
//判断持有读锁线程是否为当前线程
if (firstReader == current) {
//判断持有读锁数量是否为1
if (firstReaderHoldCount == 1)
//为1,置空firstReader
firstReader = null;
else
//不为1,将该线程持有锁数量减1
firstReaderHoldCount--;
//持有锁线程不为当前线程
} else {
//获取到该线程的HoldCounter
HoldCounter rh = cachedHoldCounter;
//判断HoldCounter是否为空,且是否为该线程的HoldCounter
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
//获取该线程持有的锁数量
int count = rh.count;
//判断是否小于1
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
//将持有锁数量减1
--rh.count;
}
//自旋确保成功
for (;;) {
//获取状态
int c = getState();
//更新状态值
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
当执行tryReleaseShared返回true后执行doReleaseShared方法,doReleaseShared方法的源码我们在AQS类分析的时候已经讲过了,故此不再赘述。
写锁
lock
写锁中的lock源码:
public void lock() {
//调用AQS类实现
sync.acquire(1);
}
//AQS类中acquire方法
public final void acquire(int arg) {
//tryAcquire由子类实现
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法在AQS类分析的时候已经分析过了。
我们来看下子类Sync中实现的tryAcquire方法:
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
//CAS更新持锁线程数量
setState(c + acquires);
//加锁成功
return true;
}
//没有线程持有写锁,判断是否应该阻塞该线程并尝试更新持锁线程数量
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
//应该阻塞,或者更新持锁线程数量失败
return false;
//成功,设置运行线程为当前线程
setExclusiveOwnerThread(current);
//加锁成功
return true;
}
writerShouldBlock方法分公平锁与非公平锁实现;
先来看下非公平锁实现:
final boolean writerShouldBlock() {
return false; // writers can always barge
}
公平锁writerShouldBlock实现:
final boolean writerShouldBlock() {
//调用AQS实现,hasQueuedPredecessors方法在前面已经讲述
return hasQueuedPredecessors();
}
unLock
public void unlock() {
//调用AQS的release方法实现
sync.release(1);
}
//AQS中release实现
public final boolean release(int arg) {
//调用子类tryRelease方法
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//将头节点解锁,并释放头节点
unparkSuccessor(h);
//解锁成功
return true;
}
return false;
}
tryRelease在AQS子类Sync中实现:
protected final boolean tryRelease(int releases) {
//判断是否是当前线程执行unLock
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
//判断读锁数量是否应该为0
boolean free = exclusiveCount(nextc) == 0;
if (free)
//为0,置空当前执行线程
setExclusiveOwnerThread(null);
//更新线程持有写锁数量
setState(nextc);
return free;
}
锁降级
锁降级指的是写锁降级成为读锁,如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这个过程是不能被称为锁降级的。锁降级是指把当前拥有的写锁,再获取到读锁,随后释放写锁的过程。
ReentrantReadWriteLock不支持锁升级(持有读锁,获取写锁,最后释放读锁的过程),目的也是为了数据可见性,如果读锁已经被多个线程获取,其中任意线程成功获取了写锁并且更新了数据,这个更新对其他已经获取到读锁的线程是不可见的。
内容总结
以上是互联网集市为您收集整理的Java并发之ReentrantReadWriteLock全部内容,希望文章能够帮你解决Java并发之ReentrantReadWriteLock所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。