读写互斥原则:
解决线程安全 问 题使用 ReentrantLock 就可以 ,但是 ReentrantLock 是独占锁 ,某时只有一个线程可以获取该锁,而实际中会有写少读多的场景,因此就需要读写锁ReentrantReadWriteLock
JUC包中的读写锁接口为ReadWriteLock:
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading
*/
Lock readLock();//返回读锁
/**
* Returns the lock used for writing.
*
* @return the lock used for writing
*/
Lock writeLock();//返回写锁
}
1. ReentrantReadWriteLock实现了ReadWritrLock接口
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
2. FairSync、NonfairSync继承Sync类,提供了公平和非公平的实现
static final class NonfairSync extends Sync{
}
static final class FairSync extends Sync {
}
abstract static class Sync extends AbstractQueuedSynchronizer{
}
3. WriteLock、Sync、ReadLock、FairSync、NonfaruSyns都是ReadWritrLock的静态内部类
AQS 中只维护了 一个 state 状态,而 ReentrantReadWriteLock 则 需要维护读状态和写状态
public abstract class AbstractQueuedSynchronizer{
private volatile int state;//state是int类型 32位
}
用 state 的高16 位表示读状态,也就是获取到读锁的次数;使用低 16 位表示获取到写锁的线程的可重入次数 。
static final int SHARED_SHIFT = 16;
//共享锁(读锁)状态单位值 65536 1<<16 2^16
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//共享锁线程最大个数 65535
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
//排它锁(写锁)掩码,二进制,15 个 1
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//返回读锁线程数
/** Returns the number of shared holds represented in count */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//返回写锁可重入个数
/** Returns the number of exclusive holds represented in count */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
//firstReader 用来记录第一个获取到读锁的线程
private transient Thread firstReader = null;
//firstReaderHoldCount 则记录第 一个获取到读锁的线程获取读锁的可重入次数
private transient int firstReaderHoldCount;
//cachedHoldCounter 用来记录最后 一个获取读锁的线程获取读锁 的可重入次数
private transient HoldCounter cachedHoldCounter;
/*
readHolds是ThreadLocal变量 ,用来存放除去第一个获取读锁线程外的其他线程获取读锁的可重入次数 。ThreadLocaHoldCounter 继承了ThreadLocal ,因而重写的initialValue 方法返回 一个 HoldCounter 对象
*/
private transient ThreadLocalHoldCounter readHolds;
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
ReentrantReadWriteLock 中 写锁使用 WriteLock 来实现
1. void lock()
public static class WriteLock implements Lock, java.io.Serializable {
public void lock() {
sync.acquire(1);
}
}
//AbstractQueuedSynchronizer类
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//Sync类
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
//c!=0说明读锁或者写锁已经被某线程获取
if (c != 0) {//代码1处
// (Note: if c != 0 and w == 0 then shared count != 0)
// w=O说明已经有线程获取了读锁
// w!=O 并且当前线程不是写锁拥有者则返回false
if (w == 0 || current != getExclusiveOwnerThread())//代码2处
return false;
//说明当前线程获取了写锁,判断可重入次数
if (w + exclusiveCount(acquires) > MAX_COUNT)//代码3处
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);//设置可重入次数 代码4处
return true;
}
//第 一个写线程获取写锁
//代码5处
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
tryAcquire方法中:
代码1处:如果当前 AQS 状态值不为 0 则说明 当前己经有线程获取到了读锁或者写锁
代码2处:
代码3处:说明当前线程之前已经获取到了该锁,所以判断该线程的可重入次数是不是超过了最大值,是则抛出异常
代码4处: 增加当前线程的可重入次数,返回true
代码5处: AQS 的状态值等于 0则说明目前没有线程获取到读锁和写锁,让第 一个写线程获取写锁
//NonfairSync类
// writerShouldBlock方法发非公平锁实现
final boolean writerShouldBlock() {
return false; // writers can always barge
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
代码对于非公平锁来说总是返回false, 因此会进行CAS尝试获取写锁,获取成功则设置当前锁的持有者为当 前线程并返回 true ,否则返回 false
//FairSync类
// writerShouldBlock方法发公平锁实现
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
使用 hasQueuedPredecessors来判断当前线程节点是否有前驱节点,如果有则当前线程放弃获取写锁的权限 ,直接返回 false, 公平在于先来先服务,有前驱节点说明当前线程前面还有其他先来的线程在排队
2. void lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
类似于lock()方法,它的不同 之处在于 ,它会对中断进行响应 ,也就是当其他线程调用了该线程的interrupt方法中断了 当 前线程时 , 当 前线程会抛出 异常 InterruptedExcep tion异常。
3. boolean tryLock( )
尝试获取写锁 非公平实现
//WriteLock类
public boolean tryLock( ) {
return sync.tryWriteLock();
}
//Sync类
final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c != 0) {
int w = exclusiveCount(c);
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
if (!compareAndSetState(c, c + 1))
return false;
setExclusiveOwnerThread(current);
return true;
}
4. void unlock()
尝试释放锁
//WriteLock类
public void unlock() {
sync.release(1);
}
//AQS类
public final boolean release(int arg) {
//调用ReentrantReadWriteLock中sync类实现的tryRelease方法
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)//激活阻塞队列里面的一个线程
unparkSuccessor(h);
return true;
}
return false;
}
//Sync类
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())//看是否是写锁拥有者调用的unlock
throw new IllegalMonitorStateException();
//获取可重入值,这里没有考虑高16位,因为获取写锁时读锁状态值肯定为0
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)//如采写锁可重入值为0则释放锁,否则只是简单地更新状态值
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
ReentrantReadWri teLock 中的读锁是使用 ReadLock 来实现的
1. void lock()
//ReadLock类
public void lock() {
sync.acquireShared(1);
}
//AQS类
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
//java.util.concurrent.locks.ReentrantReadWriteLock.Sync.tryAcquireShared(int)
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();//获取当前状态值 代码1
//代码2处
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)//判断是否写锁被占用
return -1;
int r = sharedCount(c);//获取读锁计数 代码3处
//尝试获取锁 ,多个读线程只有一个会成功,不成功的进入fullTryAcquireShared进行重试
if (!readerShouldBlock() && //代码4处
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {//第 一个线程获取读锁 代码5处
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {//如果当前线程是第一个获取读锁的线程 代码6处
firstReaderHoldCount++;
} else {
//记录最后一个获取读锁的线程或记录其他线程读锁的可重入数
HoldCounter rh = cachedHoldCounter; //代码7处
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);//代码8处
}
tryAcquireShared方法解释:
1. 代码1处首先获取了当前 AQS 的状态值
2. 代码2处查看是否有其他线程获取到了写锁,如果是则直接返回-1
3. 代码3处,得到获取到的读锁的个数 , 到这里说明目前没有线程获取到写锁 ,但是可能有线程持有读锁
4. 代码4处的中非公平锁的 readerShouldBlock 实现代码如下:
//java.util.concurrent.locks.ReentrantReadWriteLock.NonfairSync.readerShouldBlock()
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
//java.util.concurrent.locks.AbstractQueuedSynchronizer.apparentlyFirstQueuedIsExclusive()
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
如果队列里面存在一个元素,则判断第一个元素是不是正在尝试获取写锁,如果不是(!readerShouldBlock()=true
),则当前线程判断当前获取读锁的线程是否达到了最大值( r < MAX_COUNT
)。 最后执行CAS 操作将 AQS 状态值的高 16 位值增1(compareAndSetState(c, c + SHARED_UNIT)
) 。
5. 代码5处和代码6处记录第一个获取读锁的线程并统计该线程获取读锁的可重入数
6. 代码7处使用 cachedHoldCounter 记录最后一个获取到读锁的线程和该线程获取读锁的可重入数
readHolds记录了当前线程获取读锁的可重入数
7. 如果可以到代码8处,说明readerShouldBlock()=true
,说明有线程正在获取写锁,fullTryAcquireShared的代码与tryAcquireShared 类似(尝试获取共享锁),它们的不同之处在于,前者通过循环自旋获取
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
2. void lockInterruptibly()
//java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock.lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
类似于 lock方法,不同之处在于,该方法会对中断进行响应,也就是当其他线程调用了该线程的interrupt方法中断了当前线程时,当前线程会抛出异常
3. boolean tryLock()
尝试获取读锁
public boolean tryLock() {
return sync.tryReadLock();
}
4. void unlock()
//ReadLock类
public void unlock() {
sync.releaseShared(1);
}
//java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(int)
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
//java.util.concurrent.locks.ReentrantReadWriteLock.Sync.tryReleaseShared(int)
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0;
}
}
直接使用ArrayList是线程不安全的:
package innerlock;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWriteLockDemo {
private static ArrayList<String> array=new ArrayList<>();//线程不安全的list
private final ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
private final Lock readLock=lock.readLock();
private final Lock writeLock=lock.writeLock();
public void add(String e) {
writeLock.lock();
try {
array.add(e);
} finally {
writeLock.unlock();
}
}
public void remove(String e) {
writeLock.lock();
try {
array.remove(e);
} finally {
writeLock.unlock();
}
}
public String get(int index) {
readLock.lock();
try {
return array.get(index);
} finally {
readLock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<2;i++) {
new Thread(()->{
for(int j=0;j<5;j++)
array.add("str"+j);
}).start();
}
Thread.sleep(1000);
System.out.println(array);
}
}`package innerlock;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWriteLockDemo {
private ArrayList<String> array=new ArrayList<>();//线程不安全的list
private final ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
private final Lock readLock=lock.readLock();
private final Lock writeLock=lock.writeLock();
public void add(String e) {
writeLock.lock();//写锁
try {
array.add(e);
} finally {
writeLock.unlock();
}
}
public void remove(String e) {
writeLock.lock();
try {
array.remove(e);
} finally {
writeLock.unlock();
}
}
public String get(int index) {
readLock.lock();//读锁
try {
return array.get(index);
} finally {
readLock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantReadWriteLockDemo obj=new ReentrantReadWriteLockDemo();
for(int i=0;i<2;i++) {
new Thread(()->{
for(int j=0;j<5;j++)
obj.add("str"+j);
}).start();
}
Thread.sleep(1000);
System.out.println(obj.array);
}
}
`
安全版本:
package innerlock;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWriteLockDemo {
private ArrayList<String> array=new ArrayList<>();//线程不安全的list
private final ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
private final Lock readLock=lock.readLock();
private final Lock writeLock=lock.writeLock();
public void add(String e) {
writeLock.lock();//写锁
try {
array.add(e);
} finally {
writeLock.unlock();
}
}
public void remove(String e) {
writeLock.lock();//写锁
try {
array.remove(e);
} finally {
writeLock.unlock();
}
}
public String get(int index) {
readLock.lock();//读锁
try {
return array.get(index);
} finally {
readLock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantReadWriteLockDemo obj=new ReentrantReadWriteLockDemo();
for(int i=0;i<2;i++) {
new Thread(()->{
for(int j=0;j<5;j++)
obj.add("str"+j);
}).start();
}
Thread.sleep(1000);
System.out.println(obj.array);
}
}