[隐匿撕源码] 从ReentrantLock剖析AQS

前言

鼠年新的工作日开始了,新的一年新的工作学习,第一个工作日给自己定一些要求吧。
想写一个系列【隐匿撕源码】 剖析各种经典框架,工具,JDK的源码提升自己,同时给大家分享一些心得。这篇文章就作为开端,干就完事了。

从ReentrantLock开始

ReentrantLock 是JDK给我们提供的显示锁 在功能上远远强于synchronized

是功能上 不是性能上,随着JDK版本的一代又一代升级 synchronized的性能已经远远不是以前的重量级锁那么沉重

ReentrantLock源码

我们来看ReentrantLock的构造方法 有两种 传入boolean值 选择此锁是否是公平锁
默认是非公平锁,性能高 可以插队

 public ReentrantLock() {
        sync = new NonfairSync();
    }

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

创建了公平sync或者非公平sync之后 赋值给了一个变量 我们看一下这个Sync到底是何方神圣
源码如图
在这里插入图片描述
看到这里我们发现Sync是一个抽象静态内部类 他继承了一个类叫AbstractQueuedSynchronizer 这也就是我们标题的AQS
先不管AQS是什么 看一下这个类的继承结构
在这里插入图片描述
所以我们得出一个结论 RenntrantLock的所有锁行为都是基于这个Sync实现的 而这个Sync又是根据AQS实现的,那我们就真正进入AQS中探索


AbstractQueuedSynchronizer

先做总结,对AQS有一个清晰的认识:
AQS是一个同步队列,是一个双向链表。
维护了这样一个同步队列
在这里插入图片描述
他使用了模板方法设计模式 提供了大量模板方法
分为两种 一种是模板 一种是需要子类覆盖的流程方法
如果不懂模板方法的小伙伴需要补充一些设计模式的知识
下面我分类 一一列举AQS中模板方法的源码并附上注释

模板方法

  1. 独占式:
  //获取同步状态()
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

    //获取同步状态 (获取同步状态的时候可以响应中断)
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

    // 响应中断的同时具有等待超时功能
    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
                doAcquireNanos(arg, nanosTimeout);
    }

    //独占释放锁  
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
  1. 共享式
  //共享获取同步状态
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

    //共享获取同步状态(可以响应中断)
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

    //共享 尝试获取同步状态  有响应时间
    public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquireShared(arg) >= 0 ||
                doAcquireSharedNanos(arg, nanosTimeout);
    }

    //共享释放
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

流程方法(需要AQS子类去覆盖的)

  //独占式获取锁
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    //共享获取
    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }
	//独占释放
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

    //共享释放
    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }
    //表示此同步器是否处于独占模式
	  protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
    }

提一下final 的状态方法

//这是同步状态标志位
 private volatile int state;

//因为是volatile修饰的 所以不需要考虑获取的锁 
    protected final int getState() {
        return state;
    }
//这是有原子性隐患的set方法
   protected final void setState(int newState) {
        state = newState;
    }
//CAS原子操作的set方法
  protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

唔 如果不知道volatile和CAS 的需要补充一些多线程知识 后期我会加入这块博客 暂时木有

其实AQS就是这么多模板方法 有了这些方法就可以随心所欲的实现自己想要的功能的锁了

调用关系如图
在这里插入图片描述

再看ReentrantLock

我们知道了AQS的模式 ,那么这次再看ReentrantLock里面的Sync,NonfairSync,FairSync是如何实现锁的就容易很多

Sync:

  abstract static class Sync extends AbstractQueuedSynchronizer {
        abstract void lock();

        
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            //如果标记为是0 那么设置标记位 同时把当前线程记录  
            //因为是可重入锁   所以只需要记录线程就可以
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果标记为不是0 那么说明已经有人拿到了锁  判断拿到锁的是否是当前线程
            //如果是   记录重入次数  
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                //这里是重入次数不能大于 int最大值 。。
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            // 如果标记为不是0 而且拿到锁的不是当前线程  则获取锁失败
            return false;
        }

		//释放锁
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            //如果当前线程不是拿到锁的线程 则抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果计算之后是0 说明可以释放锁  设置当前线程为null 即可
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);   设置state
            return free;
        }

        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
		//Condition类
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
		
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }
   }

NonfairSync

   static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        
        final void lock() {
        //如果是未上锁状态(0) 则更改成1   并写入当前线程 
        //这段代码公平锁是没有的 因为非公平锁是可以插队
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
		//如果不是  cas失败 那就尝试获取锁
            else
                acquire(1);
        }
		
		
        protected final boolean tryAcquire(int acquires) {
        //Sync源码中有
            return nonfairTryAcquire(acquires);
        }
    }

FairSync

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }
        
        //公平锁上锁
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            //如果当前锁没上锁  
            if (c == 0) {
            	//hasQueuedPredecessors 这个方法确定当前线程必须是节点的头节点 
            	//实现了公平锁
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果当前线程是执行线程  更新状态
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

这样ReentrantLock的公平锁和非公平锁大致思路已经说完了
源码中的思路我都用注释的方式写在代码中了
接下来我们仿照Lock实现一个 可以同时有两个线程访问的锁:TwinsLock

自定义TwinsLock

public class TwinsLock implements Lock{

	private static final class Sync extends AbstractQueuedSynchronizer{
		Sync(int count){
			if (count <= 0) {
				throw new IllegalArgumentException("count must large zere");
			}
			setState(count);
		}

		@Override
		protected int tryAcquireShared(int reduceCount) {
			for(;;){
				int current = getState();
				System.out.println(Thread.currentThread().getName()+"尝试获取锁");
					int newCount = current - reduceCount;
					if (newCount<0 || compareAndSetState(current,newCount)) {
						String info = newCount<0?"因为锁状态小于0 说明当前持有锁的线程数已经足够 所以进入队列中":"因为不小于0 所以开始执行";
						System.out.println(Thread.currentThread().getName()+"获得了锁  "+ info);
						return newCount;
				}
			}
		}

		@Override
		protected boolean tryReleaseShared(int returnCount) {
			for(;;){
				int current = getState();
				int newCount = current + returnCount;
				if (compareAndSetState(current, newCount)) {
					System.out.println(Thread.currentThread().getName()+"释放了锁");
					return true;
				}

			}
		}


	}
    //这个构造方法可以是任何数量  这里是2就代表最多有两个线程持有锁
    //同时代表  有效标志位是0 1 2
	private final Sync sync = new Sync(2);

	@Override
	public void lock() {
		sync.acquireShared(1);
	}

	@Override
	public void unlock() {
		sync.releaseShared(1);

	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		sync.acquireInterruptibly(1);
	}

	@Override
	public boolean tryLock() {
		return sync.tryAcquireShared(1) > 0;
	}

	//忽略  不是重点
	@Override
	public boolean tryLock(long time, TimeUnit unit)
			throws InterruptedException {
		return false;
	}
    //忽略  不是重点
	@Override
	public Condition newCondition() {
		return null;
	}





	public static void main(String[] args) throws InterruptedException {
		final Lock lock = new TwinsLock();
		class Worker extends Thread{

			@Override
			public void run() {
				while(true){
					lock.lock();
					System.out.println("["+Thread.currentThread().getName()+"]开始执行============");
					try{
						Thread.sleep(1000);
						String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern("hh:mm:ss"));
						System.out.println(now+"  线程["+Thread.currentThread().getName()+"]正在等待");
						Thread.sleep(1000);

					} catch (InterruptedException e) {
						e.printStackTrace();
					} finally{
						lock.unlock();
					}

				}
			}

		}
		//启动10个线程
		for (int i = 0; i < 10; i++) {
			Worker w = new Worker();
			w.start();
		}


	}

}

执行结果:
在这里插入图片描述

总结

AQS使用一个同步队列 使用FIFO 先入先出
我们可以基于AQS实现各种各样的花哨功能的锁
其中ReentrantLock就是我们常用的
而且在使用过程中我们完全感受不到AQS的存在
是一个非常棒的设计
并发工具 如果闭锁 CyclicBarrier Semaphore等等都是基于AQS实现的
tip:其中CyclicBarrier依赖的是ReentrantLock 所以也算是依赖AQS

发布了24 篇原创文章 · 获赞 10 · 访问量 3073

猜你喜欢

转载自blog.csdn.net/qq_43091847/article/details/104176960
今日推荐