package com.jx.source.juc.aqs;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.concurrent.locks.LockSupport;
/**
* @Description:
* @Author: zhangjx
* @Date: 2021-08-30
**/
@Slf4j
public class MiniReentrantLock implements Lock{
/**
* 0 表示未加锁状态
* >0 表示加锁状态
*/
private volatile int state;
/**
* 当前占有锁的线程
* 独占模式 : 统一时间只有一个线程可以持有锁 ,其它的线程未获取到锁时会被阻塞
*/
private Thread exclusiveOwnerThread;
/**
* 头节点 head 节点对应的线程是占用锁的线程
*/
private Node head;
/**
*尾结点
*/
private Node tail;
static Unsafe U;
/**
* state偏移量
*/
static long stateOffset;
/**
* head偏移量
*/
static long headOffset;
/**
* tail偏移量
*/
static long tailOffset;
/**
* 阻塞的线程封装成node节点 放入fifo队列
*/
@Data
@NoArgsConstructor
static final class Node{
/**等待线程*/
Thread thread;
/**前置节点*/
Node pre;
/**后置节点*/
Node next;
public Node(Thread thread) {
this.thread = thread;
}
}
static {
try {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
U = (Unsafe) theUnsafe.get(null);
stateOffset = U.objectFieldOffset(MiniReentrantLock.class.getDeclaredField("state"));
headOffset = U.objectFieldOffset(MiniReentrantLock.class.getDeclaredField("head"));
tailOffset = U.objectFieldOffset(MiniReentrantLock.class.getDeclaredField("tail"));
} catch (Exception e) {
log.error("Unsafe 初始化异常",e);
}
}
protected final boolean compareAndSetHead(Node expectedValue, Node newValue) {
return U.compareAndSwapObject(this, headOffset, expectedValue, newValue);
}
protected final boolean compareAndSetTail(Node expectedValue, Node newValue) {
return U.compareAndSwapObject(this, tailOffset, expectedValue, newValue);
}
protected final boolean compareAndSetState(int expectedValue, int newValue) {
return U.compareAndSwapInt(this, stateOffset, expectedValue, newValue);
}
protected final int getAndIncrementState() {
return U.getAndAddInt(this, state, 1);
}
public void setHead(Node node) {
//当前线程已经是获取锁的线程了
this.head = node;
node.thread = null;
node.pre = null;
}
public int getState() {
return state;
}
public Node getHead() {
return head;
}
public Node getTail() {
return tail;
}
public Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
/**
* 模拟公平锁
*/
@Override
public void lock() {
acquire(1);
}
@Override
public void unLock() {
release(1);
}
private void release(int arg){
if(tryRelease(arg)){
Node h = this.head;
if(h.next != null){
unparkThread(h);
}
}
}
private void unparkThread(Node node){
Node s = node.next;
if(s != null && s.thread != null){
LockSupport.unpark(s.thread);
}
}
/**
* 释放锁成功返回true
* @return
*/
private boolean tryRelease(int arg){
int c = getState() - arg;
if(getExclusiveOwnerThread() != Thread.currentThread()){
throw new RuntimeException("must get lock");
}
//执行到这里不存在并发 只有持有锁的线程能够执行到这里
if(c == 0){
/**
* 锁已经完全释放了
* 1、exclusiveOwnerThread设置为空
* 2、state 设置为0
*/
this.exclusiveOwnerThread = null;
this.state = 0;
return true;
}else {
this.state = c;
}
return false;
}
/**
* 获取锁成功则返回 否则阻塞
* @param arg
*/
private void acquire(int arg){
if(!tryAcquire(arg)){
Node node = addWaiter();
acquireQueued(node,arg);
}
}
/**
*尝试抢锁失败需要做什么?
* 1、将当前线程封装成Node加入队列
* 2、需要将当前线程park掉 使当前线程处于挂起状态
* 唤醒后需要做什么?
* 1、检查当前Node节点是否为head.next节点(head.next节点拥有抢占权限 其他的线程没有抢占权限)
* 2、抢占
* 成功: 将当前node设置为head,将老的head做出队操作
* 失败:继续park 等待被唤醒
*/
private void acquireQueued(Node node,int arg){
for (;;){
Node pre = node.pre;
if(pre == head && tryAcquire(arg)){
/**
* 说明当前线程竞争锁成功
* 1、设置当前head为node
* 2、原始head出队
*/
setHead(node);
pre.next = node;
return;
}
log.info("当前线程{} 挂起",Thread.currentThread().getName());
//将当前线程挂起
LockSupport.park();
log.info("当前线程{} 唤醒",Thread.currentThread().getName());
}
}
/**
* 尝试获取锁不阻塞线程
* true 抢锁成功
* false 抢锁失败
* @param arg
* 返回false情况:
* 1、cas失败
* 2、state > 0
*/
private boolean tryAcquire(int arg){
if(state == 0){
//当state==0时 不能直接抢锁 公平锁 先来后到
if(!hasQueuedThreads() && compareAndSetState(0,arg)){
this.exclusiveOwnerThread = Thread.currentThread();
return true;
}
}else if(Thread.currentThread() == this.exclusiveOwnerThread){
//锁被当前线程占用 重入
int c = getState();
c = c + arg;
this.state = c;
return true;
}
return false;
}
/**
* 判断是否有线程等待
* 什么时候返回false
* 1、 当前队列是空
* 2、 当期线程为head.next节点线程 head.next在任何时候都有权利竞争锁
* @return
*/
private boolean hasQueuedThreads(){
Node h = head;
Node t = tail;
Node s;
/**
* 条件1: h != t
* true 说明当前队列已经有node了
* false
* 1、h == t == null 队列中没有node
* 2、h == t == head 第一个获取锁失败的线程会为当前持有锁的线程补充创建一个head节点
* 条件2:s = h.next) != null || s.thread != Thread.currentThread()
* 前置条件: 队列中有node
* 条件2.1 (s = h.next) != null
* 避免 enQueue时的并发情况
* 如当前队列已经有节点a head=a,tail=a, 队列 a ->
* 线程b cas成功 设置a为b的前驱节点 但是还未将a的后继设置为b head = a,tail = b ,队列 a <- b
* 线程c cas成功 设置b为c的前驱节点 head=a,tail=c 队列: a<- b <=> c
*
* 此时head.next虽然为null但是实际上head.next已经被预订了
*
* 条件2.2 s.thread != Thread.currentThread()
* true 当前线程是head.next节点
* false 当前线程不是head.next节点
*
* 如果是 a<=>b b为当前线程节点 返回false 此时当前线程有抢锁资格
* 如果是 a <- b<=>c c为当前线程节点 c没有抢锁资格
*/
return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}
/**
* 当前线程入队
* 返回当前线程对应的Node节点
* 入队流程:
* 队列已经有等待节点
* 1、找到newNode的前置节点 pre
* 2、更新newNode.pre = pre
* 3、CAS更新tail为newNode
* 4、更新pre.next= newNode
*
* @return
*/
private Node addWaiter(){
Node newNode = new Node(Thread.currentThread());
//此处不要用tail,用pre保存当前tail,tail可能被修改
Node pre = tail;
if(pre!= null){
newNode.pre = pre;
if(compareAndSetTail(pre,newNode)){
pre.next= newNode;
return newNode;
}
}
/**
* 执行到这里的情况
* 1、tail==null
* 2、CAS设置当前newNode为tail失败
*/
enQueue(newNode);
return newNode;
}
/**
* 自旋入队 只有成功后才返回
* 1、tail==null 空队列
* 2、CAS设置当前newNode为tail失败
* @param node
* @return
*/
private void enQueue(Node node){
for (;;){
/**
* 1、队列是空
* 说明当前线程是第一个抢锁失败的线程 不然队列应该有一个节点,即当前持有锁线程节点,而当前持有锁的线程由于没有抢锁失败,队列中没有当前持有锁的线程的节点
* 所以当前线程需要为当前持有锁线程建立节点 head节点任何时候都代表占有锁线程节点
*/
if(compareAndSetHead(null,node)){
tail = head;
}else {
//说明当前队列已经有node了,需要追加node
Node pre = tail;
if(pre!= null){
node.pre = pre;
if(compareAndSetTail(pre,node)){
pre.next= node;
return;
}
}
}
}
}
}