目录
1 读写锁规则
读-读可以共存,读-写不能共存,写-写不能共存2 读写锁使用
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 读方法
private void read() {
readWriteLock.readLock().lock();
System.out.println(Thread.currentThread() + "read start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "read end");
readWriteLock.readLock().unlock();
}
// 写方法
private void write() {
readWriteLock.writeLock().lock();
System.out.println(Thread.currentThread() + "write start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "write end");
readWriteLock.writeLock().unlock();
}
测试代码
@Test
public void readWriteLockTest() throws InterruptedException {
for (int i = 0; i < 5; i ++) {
new Thread(() -> read()).start();
}
for (int i = 0; i < 5; i ++) {
new Thread(() -> write()).start();
}
TimeUnit.MINUTES.sleep(1);
}
输出
Thread[Thread-0,5,main]read start
Thread[Thread-1,5,main]read start
Thread[Thread-2,5,main]read start
Thread[Thread-3,5,main]read start
Thread[Thread-3,5,main]read end
Thread[Thread-0,5,main]read end
Thread[Thread-1,5,main]read end
Thread[Thread-2,5,main]read end
Thread[Thread-5,5,main]write start
Thread[Thread-5,5,main]write end
Thread[Thread-6,5,main]write start
Thread[Thread-6,5,main]write end
Thread[Thread-4,5,main]read start
Thread[Thread-4,5,main]read end
Thread[Thread-7,5,main]write start
Thread[Thread-7,5,main]write end
Thread[Thread-8,5,main]write start
Thread[Thread-8,5,main]write end
Thread[Thread-9,5,main]write start
Thread[Thread-9,5,main]write end
当然每次执行结果可能不一样,但是总体可以得出结论,读读可以并发,读写不能并发,写写不呢并发3 读写锁原理
2 认识ReadWriteLock接口
java中读写锁是实现了ReadWriteLock接口,这个接口比较简单,只有两个方法
public interface ReadWriteLock {
/**
* @return 获取读锁
*/
Lock readLock();
/**
* @return 获取写锁
*/
Lock writeLock();
}
ReentrantReadWriteLock方法的实现
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
可以看到返回的是ReentrantReadWriteLock的内部类
3 写锁Sync类中基本变量
/**
* 读写锁共用同步状态变量,来同时维护读和写的状态,按位切割使用,高16位表示读的状态,低16位表示写的状态
*/
//读锁状态偏移量,左移16位16位
static final int SHARED_SHIFT = 16;
//读锁操作的基本单元,读锁状态+1,则状态变量值+SHARED_UNIT
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;
4 写锁加锁方法
public void lock() {
sync.acquire(1);
}
可以看到用到的还是AQS中的模版方法,acquireShared方法执行过程在笔者JUC并发工具二-AQS中讲过,感兴趣的同学可以先了解一下
然后看acquireShared方法中的第一步tryAcquireShared在读锁中的实现
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
// 获取当前state资源的值
int c = getState();
// 获取当前写锁状态
int w = exclusiveCount(c);
if (c != 0) {
// 如果c!=0并且w=0表示持有共享锁,获取资源失败
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
// 写锁重入次数已超过最大值
throw new Error("Maximum lock count exceeded");
// 获取资源成功,修改state变量
setState(c + acquires);
return true;
}
// 对于非公平锁,writerShouldBlock总是false;对于公平锁就是在重入锁章节中讲到的hasQueuedPredecessors方法
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
// 设置当前线程占用写锁资源
setExclusiveOwnerThread(current);
return true;
}
5 写锁解锁方法
public void unlock() {
sync.release(1);
}
AQS章节中已经讲过release方法执行过程,这里直接看tryRelease代码,和ReentrantLock代码差不多
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
// 如果不是占用线程不是当前线程直接抛异常
throw new IllegalMonitorStateException();
// 查看剩余需要解锁次数
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)
// 如果当前线程解锁完毕释放线程占用
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
6 读锁加锁方法
public void lock() {
sync.acquireShared(1);
}
可以看到用到的还是AQS中的模版方法,acquireShared方法执行过程在笔者JUC并发工具二-AQS中讲过,感兴趣的同学可以先了解一下
然后看acquireShared方法中的第一步tryAcquireShared在读锁中的实现
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
// 如果有写锁占用并且占用线程不是当前线程的话返回-1,获取资源失败
return -1;
int r = sharedCount(c);
// readerShouldBlock根据公平和非公平策略限制
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);
}
7 读锁释放
public void unlock() {
sync.releaseShared(1);
}
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
// 把firstReader掷空
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;
// 自旋更新state值
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}