AQS_1_介绍

86 阅读4分钟

AQS介绍

什么是AQS?

AQS是JUC包下面一个抽象类AbstractQueuedSynchronizer的一个缩写,这个抽象类实现了多种锁的功能,包括独占锁、 共享锁、条件锁等。 该抽象类利用模板设计模式,让开发者在需要自定义这些锁的场景下,只需要关注锁的获取和释放,不需要关注这些锁的具体实现方式。

总结:它是一个JDK提供的工具类,利用模板设计模式屏蔽了多种锁的具体实现,让开发者在自定义锁的场景下更加专注锁的获取和释放,极大的提高了锁的复用性,降低了锁实现和使用之间耦合度。

AQS解决了什么问题?

AQS降低了自定义锁的复杂度,让开发者能够不了解相关锁实现细节的情况下,自定义出符合自己业务场景的锁。 简单来说就是AQS让一些对并发知识是那么了解的选手也能够实现一把符合业务场景的锁。 还有就是AOS的源码非常优秀,给一些想要研究相关锁实现原理,并发知识的选手提供了很好的参考借鉴。

AQS的特点?

  1. 开箱即用: AQS是JDK自带的工具类,不需要依赖其它环境。
  2. 功能丰富: AQS支持多种锁,如独占锁、公平锁、非公平锁、条件锁、共享锁等等。
  3. 扩展性强:AQS使用了模板设计模式,可以很方便的扩展和复用已有的锁逻辑;实际上在JDK已经中已经有了诸多实现,如:ReentrantLock、CountDownLatch、Semaphore、CyclicBarrier、ReentrantReadWriteLock等。

AQS的使用

前面说到了AQS主要是利用模板设计模式,让开发者能够很简单的实现自己的锁,主要包括独占锁和共享锁。

AQS提供的模板方法:

 // 尝试获取独占锁
 protected boolean tryAcquire(int arg) {        throw new UnsupportedOperationException();    }
 // 尝试释放独占锁
 protected boolean tryRelease(int arg) {        throw new UnsupportedOperationException();    }
 // 尝试获取共享锁
 protected int tryAcquireShared(int arg) {        throw new UnsupportedOperationException();   }
 // 尝试释放共享锁
 protected boolean tryReleaseShared(int arg) {        throw new UnsupportedOperationException();    }
 // 当前锁是否是独占锁
 protected boolean isHeldExclusively() {        throw new UnsupportedOperationException();    }

自定义独占锁

我们利用提供的模板方法简单来实现一个不可重入的独占锁

 public class AqsExclusiveLock extends AbstractQueuedSynchronizer {
 ​
     @Override
     protected boolean tryAcquire(int state) {
         int c = getState();
         // 如果state为0,说明没有线程持有锁,尝试加锁
         if (c == 0) {
             if (compareAndSetState(0, state)) {
                 setExclusiveOwnerThread(Thread.currentThread());
                 return true;
             }
         }
         return false;
     }
 ​
     @Override
     protected boolean tryRelease(int state) {
         // 如果state为1,则说明已经加锁,则重新设置为0,释放锁
         if (compareAndSetState(state, 0)) {
             setExclusiveOwnerThread(null);
             return true;
         }
         return false;
     }
 ​
     public void lock() {
         acquire(1);
     }
 ​
     public void unLock() {
         release(1);
     }
 }
 ​
 // 测试代码
 public static void main(String[] args) throws InterruptedException {
     AqsExclusiveLock lock = new AqsExclusiveLock();
     Thread thread1 = new Thread(() -> {
         try {
             System.out.println(Thread.currentThread().getName() + " 获取锁");
             lock.lock();
             TimeUnit.SECONDS.sleep(3);
         } catch (InterruptedException e) {
             e.printStackTrace();
         } finally {
             lock.unLock();
             System.out.println(Thread.currentThread().getName() + " 释放锁");
         }
     }, "Thread1");
     thread1.start();
 ​
     TimeUnit.SECONDS.sleep(1);
     System.out.println(Thread.currentThread().getName()+" 尝试竞争锁");
     lock.lock();
     System.out.println(Thread.currentThread().getName()+" 竞争锁成功");
     lock.unLock();
 }

可以看到上面,我们只需要几行代码就可以实现一个独占锁,并不用关心线程获取锁的步骤,线程阻塞后如何唤醒,以及管理相关线程;极大的简化了自定义锁的复杂逻辑。

AQS原理介绍

AQS设计思想主要是:双向链表+共享资源state+LockSupport实现。

阻塞队列(双向链表):维护了竞争锁失败的线程,是一个FIFO的双向链表,该链表是一个个内部类Node节点,该Node是对阻塞的线程的一层包装,存储了线程的引用,以及阻塞线程的相应状态。

Node相应状态:

 /** Marker to indicate a node is waiting in shared mode */
 static final Node SHARED = new Node();// 共享模式
 /** Marker to indicate a node is waiting in exclusive mode */
 static final Node EXCLUSIVE = null;// 独占模式
 ​
 // 状态↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
 /** waitStatus value to indicate thread has cancelled. */
 static final int CANCELLED =  1;// 已取消
 /** waitStatus value to indicate successor's thread needs unparking. */
 static final int SIGNAL    = -1;// 等待唤醒
 /** waitStatus value to indicate thread is waiting on condition. */
 static final int CONDITION = -2;// 条件,用作条件锁等待标识
 /**
 * waitStatus value to indicate the next acquireShared should
 * unconditionally propagate.
 */
 static final int PROPAGATE = -3;// 共享模式

共享资源state:一个volatile修饰的int变量,在独占锁里面可代表重入次数,共享锁中代表可获取锁的线程个数。

LockSupport:阻塞线程和唤醒线程的实现API,底层也是调用mutex lock。

AQS原理和synchronized原理类似,但是AQS的功能比synchronized更加强大,毕竟AQS是一个API层面的锁实现。

image-20220605124929938.png