多线程-- 八.J.U.C之AQS

AQS

 

一.AQS的概念:

    lock包下有三个笼统的类:

        AbstractOwnableSynchronizer

        AbstractQueuedLongSynchronizer

        AbstractQueuedSynchronizer

    通常的,AbstractQueuedSynchronizer的简称为AQS。一般我们叫AQS抽象队列同步器。

    AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它    

    它是J.U.C的核心。

二.AQS的设计:

    1.使用Node实现FIFO(First in First out)队列,可以用于构建锁或者其它同步装置的基础框架。

    2.利用了一个Int类型表示状态。

    3.使用方法是继承。子类通过继承并通过实现它的方法管理其状态(acquire和release)的方法操纵状态。

    4.可以同时实现排它锁和共享锁模式。(独占,共享)

三.AQS的常用同步组件:

    1.CountDownLatch

    2.Semaphore

    3.CyclicBarrier

    4.ReentrantLock

    5.Condition(基于ReentrantLock的)

    6.FutureTask(它不是AbstractQueuedSynchronizer类的子类)

 


 

1.CountDownLatch

    它是一个同步辅助类,通过它可以完成类似于阻塞当前线程的功能,换句话说,就是一个或多个线程一直等待,直到其它线程执行的操作完成。

    它用了一个给定的计数器来进行初始化,该计数器的操作是原子性的,也就是说,同时只能有一个线程去操作计数器。

    调用该类await方法的线程,会一直处于阻塞状态,直到其它线程调用countDown方法,使当前计数值的值变为0。

    每次调用countDown方法的时候,计数器的值会减一。当计数器的值减到0的时候,所有调用await方法而处于等待状态的线程会继续往下执行。这操作只能出现一次,因为计数器是不能重置的。

    典型应用场景:并行计算。

 

举例:

    CountDownLatch countDownLatch = new CountDownLatch(200);

    200表示需要等待执行完毕的线程数量。

结果就是,finish是在所有线程执行完毕之后执行的。

 

也可以有复杂的情况,就是每个任务我都给指定时间,超过指定时间没做完,那也不管了。这也好弄,就是修改await方法

第一个参数是数值,第二个参数是单位。

 

 



 

2.Semaphore

    信号量。它可以控制并发访问的线程个数。和CountDownLatch使用有些类似,也提供了两个核心方法。acquire和release.

    acquire方法是让它获取一个许可,如果没有,就等待; release方法是执行完成后,释放一个许可出来。

    使用场景:常用于仅能提供有限访问的资源,比如数据库。数据库连接数假设只有20,而我们应用的并发数可能会远远大于这些。如果同时对数据库进行操作,有可能会出现因为无法获取数据库连接,而导致异常。这个时候我们就可以通过Semaphore控制并发数 

举例:

上图只是获取一个许可,如果我们有需求,需要拿到多个许可才可以执行怎么办?

答:也是在方法里面加参数即可:

以上图为例,假如我们想,超过3个并发,剩下的不执行了,丢弃,那么怎么办?

答:用semaphore.tryAcquire()方法即可。

 


 

 



 

3.CyclicBarrier

    也是一个同步辅助类。他允许一组线程相互等待直到某个公共的屏障点。

    通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后才能各自继续往下执行后面的操作。

    和CountDownLatch相似的地方上是,它也是通过计数器实现的。

代码举例实现:

     CyclicBarrier barrier = new CyclicBarrier(5);    这句代码告诉我们当前有5个线程要同步等待。

     barrier.await();    当达到括号里给定的数目的时候,这段代码后面的部分就可以执行了。

运行结果为:

同样,这个await方法也是可以传参的,跟上面一样,也是传入时间,意思就是让它只等待多少时间。

而且,在CyclicBarrier barrier = new CyclicBarrier(5)代码里面,也可以传入第二个参数runnable接口。例:

    CyclicBarrier barrier = new CyclicBarrier(5,()->{

        log.info("test running");

    });

意思是,在达到屏障点的时候,优先执行里面的代码。

   

 



 


 

 

4.ReentrantLock 与 锁

    Java里主要分为两类锁,一类是synchronized关键字修饰的锁;一类是J.U.C里提供的锁。

一.ReentrantLock(可重入锁)和synchronized的区别

    ①.可重入性

    ②.锁的实现

        synchronized关键字是依赖JVM实现的,ReentrantLock是JDK实现的。

    ③.性能区别

        在synchronized关键字优化以前,性能差很多。但是引入了轻量级锁之后,两者性能差不多了。

    ④.功能区别

        便利性:synchronized用起来比较方便,是由编译器决定锁的加锁和释放,不会造成死锁,JVM自动释放。而ReentrantLock需要我们手工加锁释放锁。为了避免忘记释放造成死锁,最好在finally中声明。

        锁的细粒度和灵活度:ReentrantLock要优于synchronized。

 

二.ReentrantLock独有的功能

    ①.ReentrantLock可指定是公平锁还是非公平锁;而 synchronized只能是非公平锁。

        公平锁:先等待的线程先获得锁。

    ②.ReentrantLock提供了Condition类,可以分组唤醒需要唤醒的线程;而synchronized要么随机唤醒一个,要么唤醒全部。

    ③.ReentrantLock提供了能够中断等待锁的线程的机制,lock.lockInterruptibly()

    所以,如果想实现这三个功能的时候,是必须要使用ReentrantLock的。

 

代码演示:

    

 

Lock lock = new ReentrantLock()创建锁对象时,可以传入参数。

源码如下:

默认传入的是个非公平锁。可以传入参数true false使其成为公平锁。

   

        

 


 

5.StampedLock

    StampedLock控制锁有三种模式。写,读,乐观读。重点在乐观读上。

    一个StampedLock状态是由版本和模式两个部分组成。锁获取方法返回的是一个数字,作为票据。0表示没有写锁,读锁上分为悲观锁和乐观锁。

    对吞吐量有巨大改进,性能高一些。  

 

    举例:

    

    

 


 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值