package demo;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/***
* 无界队列锁,从头部入队列的操作不是很优雅,
* 所以考虑的操作为:得到执行权的线程战友头节点,但是头节点不指向任何一个线程,凡是获得执行权的线程所在的节点的所有成员变量都将被置为空
* */
public class LinkedQueueLock {
//默认的自旋时间,单位是毫秒
public static final int DEFAULT_SPIN_TIMES=50;
//自定义自旋时间
private int spinTimes;
//是否为公平锁
private boolean isFair;
//锁状态
private AtomicInteger state=new AtomicInteger(0);
//链表的头节点
private AtomicReference<Node> header=new AtomicReference<Node>(null);
//链表的尾节点
private AtomicReference<Node> tail=new AtomicReference<Node>(null);
//独占线程
private AtomicReference<Thread> mainThread=new AtomicReference<Thread>(null);
//构造方法
public LinkedQueueLock(){
this(DEFAULT_SPIN_TIMES, false);
}
public LinkedQueueLock(int spinTimes){
this(spinTimes,false);
}
public LinkedQueueLock(boolean isFair){
this(DEFAULT_SPIN_TIMES,isFair);
}
public LinkedQueueLock(int spinTimes,boolean isFair){
if(spinTimes<0)
throw new RuntimeException("自旋参数为负值");
this.spinTimes=spinTimes;
this.isFair=isFair;
//初始化时头部和尾部共享同一个空的节点,这个节点代表获得执行权的节点,这样当有线程发生非公平竞争的时候,不需要从头部进入整个队列
Node node = new Node();
node.setSpinParam(false);
header.set(node);
tail.set(node);
}
//线程入队列的方法,cas加死循环
private void enq(Node node){
while(true){
Node oldTailNode = tail.get();
if(tail.compareAndSet(oldTailNode, node)){
System.out.println("线程"+Thread.currentThread().getName()+"进入自旋队列");
oldTailNode.setNextNode(node);
node.setPreNode(oldTailNode);
break;
}
}
}
//自旋方法,根据用户设置的睡眠时间自旋
private void spin(Node node){
System.out.println("线程"+Thread.currentThread().getName()+"开始自旋");
while(node.getSpinParam()){
try{
System.out.println("线程"+Thread.currentThread().getName()+"正在自旋");
Thread.sleep(this.spinTimes);
}
catch(Exception e){
//忽略
}
}
}
//获取锁失败后处理节点的方法,包括先入队列,再自旋,自旋后再获取锁
private void proNode(Node node){
//入队列
enq(node);
//外层循环,用于自旋后获取锁失败后再回到自旋
out:while(true){
//自旋
spin(node);
System.out.println("线程"+Thread.currentThread().getName()+"结束自旋");
//自旋被唤醒后需要重新竞争锁,如果不能获取到锁则需要自己将自己的node的自旋变量的值改为true
if(tryLock()){
System.out.println("线程"+Thread.currentThread().getName()+"已经获取到了锁");
//获取到锁后需要将自己的节点设置为头节点,将独占线程设置为自己当前执行线程
mainThread.set(Thread.currentThread());
node.setPreNode(null);
node.setSpinParam(false);
node.setT(null);
in:while(true){
Node oldHeader = header.get();
header.compareAndSet(oldHeader, node);
break out;
}
}
else{
//重新自旋
node.setSpinParam(true);
}
continue out;
}
}
public void lock(){
if(tryLock()){
System.out.println("线程"+Thread.currentThread().getName()+"获取到了锁");
//获取到了锁,无需操作队列,因为头节点代表已经获取到执行权的线程
setMainThread(Thread.currentThread());
}
else{
System.out.println("线程"+Thread.currentThread().getName()+"获取锁失败");
//没有获取到执行权则需要入队列,并且自旋
Node node = new Node();
node.setT(Thread.currentThread());
//处理这个线程代表的节点,入队列,自旋,竞争锁
proNode(node);
}
}
public void unlock(){
if(state.get()==1){
//将独占线程设置为null
setMainThread(null);
/***
* 此处应该先做锁的释放然后再操作整个的自旋队列
* 因为如果将操作顺序反过来的话,当正在释放锁的线程已经将下一个线程的自旋状态置为非自旋,
* 同时有另外一个线程将自己的node添加至自旋队列,会造成这个刚进入队列的线程永远无法被唤醒
* */
//释放锁
relese();
System.out.println("线程"+Thread.currentThread().getName()+"完全释放了锁");
//此处只需要设置下一个节点的自旋状态为false即可,如果下一个节点不能竞争到锁的话则会再次自旋
Node nextNode = header.get().getNextNode();
if(nextNode!=null){
//System.out.println("线程"+Thread.currentThread().getName()+"释放锁之后将线程"+nextNode.getT().getName()+"的自旋状态置为false");
nextNode.setSpinParam(false);
}
}
else{
//出锁一次,锁状态减一
state.decrementAndGet();
}
}
//尝试获取锁
private boolean tryLock(){
return state.compareAndSet(0, 1);
}
private boolean relese(){
return state.compareAndSet(1, 0);
}
public void setHeader(Node node) {
header.set(node);
}
public Node getHeader() {
return header.get();
}
public void setTail(Node node) {
tail.set(node);
}
public Node getTail() {
return tail.get();
}
class Node{
private AtomicBoolean spinParam=new AtomicBoolean(true);
private AtomicReference<Thread> t=new AtomicReference<Thread>(null);
private AtomicReference<Node> nextNode=new AtomicReference<Node>(null);
private AtomicReference<Node> preNode=new AtomicReference<Node>(null);
public void setNextNode(Node nextNode) {
this.nextNode.set(nextNode);
}
public Node getNextNode() {
return this.nextNode.get();
}
public void setT(Thread t) {
this.t.set(t);;
}
public Thread getT() {
return this.t.get();
}
public void setSpinParam(boolean b) {
this.spinParam.set(b);;
}
public boolean getSpinParam() {
return this.spinParam.get();
}
public void setPreNode(Node preNode) {
this.preNode.set(preNode);;
}
public Node getPreNode() {
return this.preNode.get();
}
}
public void setMainThread(Thread mainThread) {
this.mainThread.set(mainThread);
}
public Thread getMainThread() {
return this.mainThread.get();
}
}