java并发编程 深入理解AQS及相关实现(六)

8 篇文章 0 订阅

AbstractQueuedSynchronizer

先对AQS的各种特点有个印象,后面执行流程讲解完了再回来对应一下就能理解了

什么是AQS?

        字面意思理解:抽象队列同步器,是一个抽象同步框架

        java中大多数同步器如Lock,Latch,Barrier等,都是基于AQS框架来实现的

        共同行为如:等待队列、条件队列、独占获取、共享获取等,也都是基于AQS实现

                实现方式:

                1.维护一个内部类Sync继承AQS

                2.将同步器所有调用都映射到Sync对应的方法

技术原理:

        基于一个共享变量state + 双向虚拟队列FIFO

 AQS特性:

        阻塞等待队列        共享/独占        公平/非公平        可重入        可中断

状态的访问方式

        getState()      setState       compareAndSetState()---比较后设置,cas方式

AQS中节点状态:

        1.值为0,初始化状态,表示当前节点在sync队列中,等待着获取锁。

        2.CANCELLED,值为1,表示当前的线程被取消

        3.SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;

        4.CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;

        5.PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;

 资源共享方式:

        独占,只有一个线程执行,如ReentrantLock

        共享,多个线程共同执行,如Semaphore/CountDownLatch

AQS实现原理:以ReentrantLock为例

     从上面这个代码来进行调试,启动两个线程,当线程1启动时,调用lock.lock()

也就是说,调用的应该是Sync类下面的lock,实现就两种,公平和非公平,默认无参构造方法使用的是非公平锁,所以面试问到记得 ReentrantLock默认是非公平的

 再看非公平的lock实现

 采用比较设置的方式,也就是常说的cas,内部调用unsafe工具包直接操作内存地址的

 如果能获取到锁,修改state,继续往下执行。但是现在线程二来了,线程一还在处理业务,并没有释放锁资源,线程二执行lock的时候获取不到,则进入阻塞队列等待

大流程并不复杂,只是里面内容比较多,还有设计类似重入(同一个线程获取了多次锁称为重入),锁释放,公平非公平(先到先得公平,随机或者后到先得非公平)唤醒(唤醒原理也是内容比较多的,可自行了解)等等,没有必要说的太深,太理论了很多朋友也不愿意去了解,大概明白原理,概念,知道怎么用就行了,下面贴一张别人的AQS的流程图,本人画图太烂,就不献丑了

 总结一下,AQS就是通过state来确定锁状态,抢不到锁的线程,就放在FIFO队列中进行管理,争取下次再进行争取获取资源

ReentrantLock

        字面理解为可重入锁,功能和synchronized是差不太多的,当然也经常有人面试被问到他两的区别:

1.synchronized是JVM层次的锁实现,ReentrantLock是JDK层次的锁实现;

2.synchronized的锁状态是无法在代码中直接判断的,但是ReentrantLock可以通过ReentrantLock#isLocked判断;

3.synchronized是非公平锁,ReentrantLock可以是公平也可以是非公平的

4.synchronized是不可以被中断的,而ReentrantLock#lockInterruptibly方法是可以被中断的;

5.在发生异常时synchronized会自动释放锁,而ReentrantLock需要开发者在finally块中显示释放锁;

6.ReentrantLock获取锁的形式有多种:如立即返回是否成功的tryLock(),以及等待指定时长的获取,更加灵活;

7.synchronized在特定的情况下对于已经在等待的线程是后来的线程先获得锁,而ReentrantLock对于已经在等待的线程是先来的线程先获得锁;

使用方式在上面已经有例子了,关于其他的重入,中断等等方式可以自行了解

ReentrantReadWriteLock

        ReentrantReadWriteLock是可重入的读写锁实现类。在它内部,维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有Writer线程,读锁可以由多个Reader线程同时持有。也就是说,写锁是独占的,读锁是共享的。

        用法差不多,就是分为读锁和写锁分开写,适合读多写少的场景

 

CountDownLatch

        CountDownLathch(闭锁)是一个同步协助类,允许一个或多个线程等待,直到其他线程完成操作集。

        CountDownLatch使用给定的计数值(count)初始化。await方法会阻塞直到当前的计数值(count)由于countDown方法的调用达到0,count为0之后所有等待的线程都会被释放,并且随后对await方法的调用都会立即返回。这是一个一次性现象——count不会被重置。如果你需要一个重置count的版本,那么请考虑使用CyclicBarrier

CountDownLatch应用场景

        CountDownLatch一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch的初始化决定)任务执行完成。

CountDownLatch与Thread.join的区别

        CountDownLatch的作用就是允许一个或多个线程等待其他线程完成操作,看起来有点类似join()方法,但其提供了比join()更加灵活的API。

        CountDownLatch可以手动控制在n个线程里调用n次countDown()方法使计数器进行减一操作,也可以在一个线程里调用n次执行减一操作。而join()的实现原理是不停检查join线程是否存活,如果join线程存活则让当前线程永远等待。所以两者之间相对来说还是CountDownLatch使用起来较为灵活

CyclicBarrier

        CyclicBarrier介绍字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态(屏障点)之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。

CyclicBarrier应用场景

        CyclicBarrier可以用于多线程计算数据,最后合并计算结果的场景。

        利用CyclicBarrier的计数器能够重置,屏障可以重复使用的特性,可以支持类似“人满发车”的场景

CyclicBarrier与CountDownLatch的区别

        1.CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次

        2.CyclicBarrier还提供getNumberWaiting(可以获得CyclicBarrier阻塞的线程数量)、isBroken(用来知道阻塞的线程是否被中断)等方法。

        3.CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。

        4.CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同。CountDownLatch一般用于一个或多个线程,等待其他线程执行完任务后,再执行。CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。

        5.CyclicBarrier还可以提供一个barrierAction,合并多线程计算结果。

        6.CyclicBarrier是通过ReentrantLock的"独占锁"和Conditon来实现一组线程的阻塞唤醒的,而CountDownLatch则是通过AQS的“共享锁”实现

Semaphore

        Semaphore,俗称信号量,Semaphore的功能非常强大,大小为1的信号量就类似于互斥锁,通过同时只能有一个线程获取信号量实现。大小为n(n>0)的信号量可以实现限流的功能,它可以实现只能有n个线程同时获取信号量。

应用场景

        可以用于做流量控制,特别是公用资源有限的应用场景

关于AQS常用实现的几个同步器就介绍到这儿,比较理论,用法都不难,但是用好就需要了解他底层源码如何实现

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值