并发编程(十):AQS

  AQS全称为AbstractQueuedSynchronizer,是并发容器中的同步器,AQS是J.U.C的核心,它是抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类都依赖它,如ReentrantLock、Semaphore、CyclicBarrier、ReentrantLock、Condition、FutureTask等。

 

  AQS的特点:

  a、使用Node实现FIFO队列,可以用于构建锁或者其他同步装置的基础框架

  b、利用一个int类型表示状态

  c、使用方法是继承

  d、子类通过继承并通过实现它的方法管理其状态

  e、可以同时实现排它锁和共享锁模式(独占、共享)

 

  AQS实现原理:

  AQS维护了一个volatile int state和一个FIFO线程等待队列

  state的访问方式有三种:getState(),setState(),compareAndSetState()

  AQS定义两种资源共享方式,Exclusive(独占,只有一个线程能执行),Share(共享,多个线程可同时执行)

  自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,具体线程等待队列的维护,如获取资源失败入队,唤醒出队等,AQS在顶层实现好了。自定义同步器实现时主要实现以下几种方法;

  tryAcquire(int):独占方式,尝试获取资源,成功返回true,失败返回false

  tryRelease(int): 独占方法,尝试释放资源,成功则返回true,失败则返回false

  tryAcquireShared(int):共享方式,尝试获取资源,负数表示失败,0表示成功,但没有剩余可用资源,正数表示成功,且有剩余资源

  tryReleaseShared(int):共享方式,尝试释放资源,如果释放后允许唤醒后续等待节点返回true,否则返回false 

 

  acquire(int)

  a、tryAcquire()尝试直接去获取资源,如果成功则直接返回;

  b、addWaiter() 将该线程加入等待队列的尾部,并标记为独占模式

  c、acquireQueueed() 使线程在等待队列中休息,有机会时会去尝试获取资源,获取到资源后才返回,如果在正在等待过程中被中断过,则返回true,否则返回false

  d、如果线程在等待过程中被中断过,它是不响应的,只是获取资源后才进行自我中断selfInterrupt(),将中断补上

 

  release(int)

  此方法是独占模式下线程释放共享资源的顶层入口,它会释放指定量的资源,如果彻底释放了(state=0),它会唤醒等待队列里的其他线程来获取资源

 

  acquireShared(int)

  此方法是共享模式下线程获取共享资源的顶层入口,它会获取指定量的资源,获取成功则直接返回,获取失败则进入等待队列,直到获取到资源为止, 跟独占模式比这里只有线程是head.next时,才会去尝试获取资源,有剩余的话还会唤醒之后的队友,假如老大用完后释放了5个资源,而老二需要6个,老三需要1个,老四需要两个,老大先唤醒老二,老二一看资源不够,他是把资源让给老三呢还是不让,答案是老二会继续park(),等待其他线程释放资源,也更不会去唤醒老三和老四了,独占模式同一时刻只有一个线程去执行,但共享模式下,多个线程是可以同时执行的,因为老二的资源需求量大,而把后面量小的老三和老四也都卡住了,它跟acqure()流程大同小异,只不过多了个自己拿到资源后,还会去唤醒后继队友的操作

 

  releaseShared()

  此方式是共享模式下线程释放共享资源的顶层入口,它会释放指定量的资源,如果成功释放且允许唤醒等待线程,它会唤醒等待队列里的其他线程来获取资源,也就是释放掉资源后,唤醒后继

 

  下面我们介绍一下通过AQS实现的类的例子,CountDownLatch、Semaphore、CyclicBarrier、ReentrantLock等都是通过AQS实现的,其中CountDownLatch和Semaphore我们已经在前面的博客中说过了,我们着重来看剩下的两个类

 

  CyclicBarrier

  它允许一组工作线程相互等待,直到到达某个工作屏障点,只有当每个线程都准备就绪后才能继续执行后面的操作,它和CountDownLatch有相似的地方都是通过计数器实现的,但它在释放等待线程后可以重用,是循环屏障,可以一直循环来使用(计数器可重置)。CountDownLatch是一个或多个线程等待一个线程的关系,CyclicBarrier主要是实现了多个线程之间相互等待,直到所有线程都满足条件后才能继续后续的操作,如有五个线程在等待,只有这5个线程都调用了await()方法后才能继续执行

并发编程(十):AQS

 

 

  CyclicBarrier-demo1

@Slf4j public class CyclicBarrierExample1 { //定义有多少线程同步等待 private static CyclicBarrier barrier = new CyclicBarrier(5); public static void main(String[] args)throws Exception { ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int threadNum = i; Thread.sleep(1000); executor.execute(()->{ try { race(threadNum); } catch (Exception e) { log.error("exception",e); } }); } executor.shutdown(); } private static void race(int threadNum)throws Exception { Thread.sleep(1000); log.info("{} is ready",threadNum); barrier.await(); log.info("{} continue",threadNum); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygppp.html