package aqs;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
public class RetreeWirterReadLock {
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
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; }
public AtomicInteger state = new AtomicInteger(0);
public AtomicInteger getState() {
return state;
}
private volatile Thread owner = null;
private volatile LinkedBlockingQueue<WaitNode> waiters = new LinkedBlockingQueue();
public void lock(){
// 尝试获取锁, 获取锁失败
if(!tryLock()){
// 添加到队列里
waiters.offer(new WaitNode(Thread.currentThread(), 0, 0));
// 避免假唤醒
while (true){
// 当线程重新苏醒,重新开始抢锁, 抢锁的条件,等待的队列的头部是自己,
WaitNode head = waiters.peek();
// 是自己
if(head != null && head.getThread() == Thread.currentThread()){
// 头部是自己。开始获取到锁
if(tryLock()){
// 获取锁成功 ,移除头部,并返回
waiters.poll();
return;
}else {
// 获取锁失败,场景: 有一个新的线程和头线程一起抢锁,头线程失败,再度休眠
LockSupport.park(Thread.currentThread());
}
} else {
// 不是自己,休眠起来,触发的场景: 1. 进来抢锁失败,并且头部不是自己,只能把自己休眠起来,2 . 线程的伪唤醒,使用循环,重新把自己休眠起来
LockSupport.park(Thread.currentThread());
}
}
}
}
/**
* 尝试获取到一个锁
* @return
*/
private boolean tryLock(){
int countNum = state.get();
// 转换成写锁的count 存在读锁,获取写锁失败
if(sharedCount(countNum) > 0){
return false;
}
// 转换成写锁的count
int count = exclusiveCount(countNum);
// 没有锁
if(countNum == 0){
// 说明下int 的前16位存放共享锁信息,后16位存放独占锁信息, 所以独占锁每次获取到一次都是 + 1
if(state.compareAndSet(0, 1)){
owner = Thread.currentThread();
return true;
}
}else if(owner == Thread.currentThread()){
// 说明下int 的前16位存放共享锁信息,后16位存放独占锁信息, 所以独占锁每次获取到一次都是 + 1
// 重入锁的情况 count +1
state.compareAndSet(countNum, countNum + 1);
return true;
}
return false;
}
public boolean unLock(){
// 释放锁
if(tryUnLock()){
// 释放锁成功, 获取到等待队列中的头线程
WaitNode head = waiters.peek();
if(head != null){// 若头部有等待的线程,则将这个线程唤醒
// 唤醒头线程
LockSupport.unpark(head.getThread());
}
return true;
}
return false;
}
/**
* 尝试释放独占锁,只会有一个线程执行到这个方法
* @return
*/
private boolean tryUnLock(){
// 前置检查, 当前的线程是否与owner是一个
if(Thread.currentThread() != owner){
throw new RuntimeException("异常情况");
}
//释放锁分为两个情况,释放锁次数,次数为0, 释放锁次数次数不为0(重入的情况)
int countNum = state.get();
// 转换成写锁的count
int count = exclusiveCount(countNum);
state.set(count - 1);// 这里不使用cas操作,是因为独占锁,释放锁,只会有一个线程进行操作
if(state.get() == 0){
//锁释放完全
owner = null;
return true;
}
return false;
}
public void lockShare(){
// 获取锁失败
if(!tryLockShare()){
// 将线程放到队列中
// 添加到队列里
waiters.offer(new WaitNode(Thread.currentThread(), 1, 0));
while (true){
// 获取到队列的头
WaitNode head = waiters.peek();
if(head != null && head.getThread() == Thread.currentThread()){
//开始尝试获取锁
if(tryLockShare()){// 获取锁成功
// 移除头部
waiters.poll();
// 唤醒接下来的读锁
WaitNode headNew = waiters.peek();
if(headNew != null && headNew.getType() == 1){
LockSupport.unpark(headNew.getThread());
}
return;
} else {
// 获取锁失败,再度把自己休眠
LockSupport.park(Thread.currentThread());
}
}else {
// 头不是自己,休眠自己
LockSupport.park(Thread.currentThread());
}
}
}
}
private boolean tryLockShare(){
// 获取到共享锁,这里可能存在多个读锁同时修改state的值,使用循环cas操作修改state ,
while (true){
int count = state.get();
// 转换成独占锁个数
int countWriter = exclusiveCount(count);
// 判断是否具有独占锁
if(countWriter != 0){
return false;
}
// 说明下int 的前16位存放共享锁信息,后16位存放独占锁信息, 所以共享锁每次获取到一次都是 + 65536
if(state.compareAndSet(count, count + SHARED_UNIT)){
return true;
}
}
}
public boolean unlockShare(){
//释放锁成功
if(tryUnlockShare()){
WaitNode head = waiters.peek();
if(head != null){
LockSupport.unpark(head.getThread());
}
return true;
}
return false;
}
public boolean tryUnlockShare(){
while (true){
int count = state.get();
if(state.compareAndSet(count, count - SHARED_UNIT)){
return state.get() == 0;
}
}
}
}
AQS就是描述上面的过程的技术,主要包括这些方法
- 获取独占锁:主要使用了尝试获取独占锁的方法,失败,就把这些锁的线程进行等待(主要实现的是线程间的挂起逻辑)
- 尝试获取独占锁: 通过判断独占锁的次数以及owner来判断是否有独占锁,若有共享锁也不能获取,使用CAS修改state
- 释放独占锁:判断state是否为0,为0进行唤醒线程(主要实现的是线程的唤醒逻辑)
- 尝试释放独占锁:进行CAS修改state,
- 获取共享锁:主要使用了尝试获取共享锁的方法,失败,就把这些锁的线程进行等待(主要实现的是线程间的挂起逻辑),成功,就唤醒队列中的读线程
- 尝试获取共享锁:通过判断独占锁的次数来判断是否有独占锁,有独占锁不能获取,有共享锁能获取,使用CAS修改state
- 释放共享锁 判断state是否为0,为0进行唤醒线程(主要实现的是线程的唤醒逻辑)
- 尝试释放共享锁 进行CAS修改state,
在上面的实现,需要用一个变量同时存储读锁次数,写锁次数在能处理读锁,和写锁同时竞争锁的情况。以下是这个同步状态的设计方案,就可以在一个变量上保存了读锁次数,和写锁次数,这是设计读写锁的必要条件