实现一个简单的无界队列锁

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();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值