七日打卡:AQS应用和Lock理解

613 阅读4分钟

在多个线程对共享资源进行争夺占用的时候,我们会首先想到加锁处理。那么我们常用的ReentrantLock就可以解决线程并发的问题,那么它的底层又是怎么实现的呢?是通过改变线程的状态来进行加锁的?还是获取资源的时候 根据资源的status来进行加锁的呢?首先我们先通过一段代码来理解下加锁的大体逻辑。

// 模拟对业务代码的加锁和解锁过程 来理解AQS
		ReentrantLock lock = new ReentrantLock(false);//创建一个非公平锁
		// 多个线程T1 T2 T3 ...
		lock.lock();
		while (true){
			if(加锁成功){//cas 来进行保证加锁成功的原子性
			break;
			}
			// 将未加锁成功的线程阻塞 存入Hashset  或者LinkedBlockingQueue
			LinkedBlockingQueue.add(T1);
			HashSet.add(T1);
			LockSupport.park();
		}
		// 执行业务逻辑代码
		lock.unlock();
		//从队列中或者set中拿取出线程 取消阻塞 在进行循环获取锁
		Thread t=HashSet.get();
		Thread t=LinkedBlockingQueue.take();
		LockSupport.unpark(t);

从上面的代码我们其实就可出lock的底层四大核心:

    1:自旋。
    2:LockSupport
    3:CAS
    4:队列 (保存被阻塞的线程 并且队列天然的FIFO特性 可以保证实现公平锁)
    

ReentrantLock 公平与非公平锁的实现

  • 默认的ReentrantLock是一个非公平锁。也即是在共享资源被释放后,所有的抢夺线程会在次一同去抢夺资源,不会按照一个先后的顺序去获取资源。在new Reenrantlockd(true)此时是一个公平锁,那么在第一次线程抢夺共享资源失败后,其他不成功的所有线程都会进行一个排队处理,在共享资源可被再次使用时,会按照排队的顺序去依次获取资源。
  • Reentrantlock中的定义了Sync,通过继承Sync进行实现FairSync、NonfairSync。同时Sync由继承了AbstractQueuedSynchronizer 所以 Reenrantlock的公平锁和非公平锁是同伙AQS来实现的。如下图所示:

AbstractQueuedSynchronizer

  • AQS具备特性

    • 阻塞等待队列
    • 共享/独占
    • 公平/非公平
    • 可重入
    • 允许中断 除了Lock外,Java.concurrent.util当中同步器的实现如Latch,Barrier,BlockingQueue等, 都是基于AQS框架实现 一般通过定义内部类Sync继承AQS 将同步器所有调用都映射到Sync对应的方法。

    AQS内部维护属性volatile int state (32位) state表示资源的可用状态 State三种访问方式 getState()、setState()、compareAndSetState() AQS定义两种资源共享方式:

    • Exclusive-独占:只有一个线程能执行,如ReentrantLock。
    • Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch AQS定义两种队列 同步等待队列 条件等待队列 不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共 享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/ 唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:
      • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去 实现它。
      • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回 false。
      • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回 false。
      • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成 功,但没有剩余可用资源;正数表示成功,且有剩余资源。 tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续 等待结点返回true,否则返回false。
  • AQS中的基本参数的意义

    • exclusiveOwnerThread : AbstractQueuedSynchronizer 继承的超类AbstractOwnableSynchronizer中的 exclusiveOwnerThread ,此变量类型是个thread类型,表示的是在独占模式下保存的是当前获取到锁的线程。
    • state
       /**
     * The synchronization state.
     */
    private volatile int state;
    

    ! state是同步器状态值,默认值是0。在线程获取到同步器锁的时候,state加1。 因为是一个可重入锁,所以state的状态会随着加锁的次数增加而增加。锁的实现是依赖于这个state的变化来判断的。

    • static final class Node:通过这个内部类来实现的双向队列(CLH),来进行保存阻塞的线程。
    • private transient volatile Node head:head属性会指向构造出的双向队列的头部。
    • private transient volatile Node tail;:head属性会指向构造出的双向队列的尾部。