java并发关键字

8 篇文章 1 订阅
1 篇文章 0 订阅

一、CountDownLatch

        CountDownLatch类位于java.util.concurrent包下,是一个同步工具类 ,允许一个或多个线程一直等待其他线程的操作执行完后再执行相关操作。
        CountDownLatch 基于线程计数器来实现并发访问控制,主要用于主线程等待其他子线程都执行完毕后执行相关操作。其使用过程为:在主线程中定义CountDownLatch,并将线程计数器的初始值设置为子线程的个数,多个子线程并发执行,每个子线程在执行完毕后都会调用countDown函数将计数器的值减1,直到线程计数器为0,表示所有的子线程任务都已执行完毕, 此时countDownLatch上等待的主线程将被唤醒并继续执行。
        我们利用CountDownLatch可以实现类似计数器的功能 比如有一个主任务,它要等待其他两个任务都执行完毕之后才能执行,此时就可以利用 CountDownLatch来实现这种功能。具体实现如下:

 

        以上代码片段先定义了一个大小为2的CountDownLatch,然后定义了两个子线程并启动该子线程,子线程执行完业务代码后在执行latch.countDown()时减少一个信号量,表示自己已执行完成。 主线程调用latch.await()阻塞等待,在所有线程都执行完成并调用了countDown函数时, 表示所有线程均执行完成,这时程序会主动唤醒主线程井开始执行主线程的业务逻辑。

二、CyclicBarrier

        CyclicBarrier (循环屏障)是一个同步工具,可以实现让一组线程等待至某个状态之后再全部同时执行。 在所有等待线程都被释放之后,CyclicBarrier 可以被重用。CyclicBarrer的运行状态叫作Barrier状态,在调用 await方法后,线程就处于Barrier状态。
        CyclicBarrier中最重要的方法是 await 方法,它有两种实现。

  • public int await():挂起当前线程直到所有线程都为 Barrier 状态再同时执行后续的任务。
  • public int await(long timeout, Tim Unit unit):设置一个超时时间,在超时时间过后,如果还有线程未达到 Barrier 状态,则不再等待,让达到 Barrier 态的线程继续执行后续的任务。

CyclicBarrier的具体使用方法如下:

        以上代码先定义了一CyclicBarrier, 然后循环启动了多个线程,每个线程都通过构造函数将 CyclicBarrier 传入线程 ,在线程内部开始执行第1阶段的工作,比如查询数据等;等第1阶段的工作处理完成后,再调cyclicBarrier.await 方法等待其他线程也完成第一阶段的工作(CyclicBarrier让一组线程等待到达某个状态再一起执行);等其他线程也执行完第1阶段的工作,便可执行并发操作的下一项任务,比如数据分发等。

 三、Semaphore

        Semaphore指信号量,用于控制同时访问某些资源的线程个数,具体做法为通过调用acquire()获取一个许可,如果没有许可,则等待,在许可使用完毕后通过 release() 释放该许可,以便其他线程使用。
        Semaphore常被用于多线程需要共享有限资源的情况,比如办公室有两台打印机,但是有5个员工需要使用, 一台打印机同时只能被一个员工使用,其他员工排队等候,且只有该打印机被使用完毕并释放后其他员工方可使用,这时就可以通过Semaphore来实现:

        以上代码中首先定义了一个数量为2的Semaphore ,然后定义了一个工作线程Worker并通过构造函数将Semaphore传入到线程内部。在线程调用semaphore.acquire()时开始申请许可并执行业务逻辑,在线程业务逻辑执行完成后调用Semaphore.release()释放许可,以便其他线程使用。

Semaphore 类中有以下几个比较重要的方法。

  • public void acquire ():以阻塞方式获取一个许可,在有可用许可时返回该许可,在没有可用许可时阻塞等待,直到获得许可。
  • public void acquire(int permits):同时获取permits个许可。
  • public void relase (): 释放某个许可。
  • public void release(int permits): 释放 permits 个许可。
  • public boolean try Acquire():以非阻塞方式获取一个许可,在有可用许可时获取该许可并返回true,否则返回false不会等待。
  • public boolean tryAcquire(long timeout,Tim Unit unit):如果在指定的时间内成功获取到可用许可,则返回true,否则返回false。
  • public boolean tryAcquire(int permits ):如果成功获取 permits 个许可 则返回 true,否则立即返回false。
  • public boolean tryAcquire(int permits,long timeout,TimeUnit unit):如果在指定的时间内成功获取permits个许可,则返回true,否则返回false。
  • availablePermits():查询可用的许可数量。

CountDownLatch、CyclicBarrier、Semaphore的区别如下。

  • CountDownLatch和CyclicBarrier 都用于实现多线程之间的相互等待,但二者的关注点不同。CountDownLatch主要用于主线程等待其他子线程任务均执行完毕后再执行接下来的业务逻辑单元,而CyclicBarrier主要用于一组线程互相等待大家都达到某个状态后,再同时执行接下来的业务逻辑单元。此外,CountDownLatch是不可以重用的,而CyclicBarrier是可以重用的。
  • Semaphore和Java中的锁功能类似,主要用于控制资源的并发访问。

 四、volatile 关键字的作用

        Java 除了使用了synchronized保证变量的同步,还使用了稍弱的同步机制,即volatile变量。 volatile也用于确保将变量的更新操作通知到其他线程。
        volatile具备两种特性:一种是保证该变量对所有线程可见,在一个线程修改了变量的值后,新的值对于其他线程是可以立即获取的;一种是volatile禁止指令重排,即volatile 不会被缓存在寄存器中或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
        因为在访问volatile变量时不会执行加锁操作,也就不会执行线程阻塞,因此volatile 变量是一种比synchronize关键字更轻量级的同步机制。volatile 主要适用于一个变量被多个线程共享,多个线程均可针对这个变量执行赋值或者读取的操作。
        在有多个线程对普通变量进行读写时,每个线程都首先需要将数据从内存中复制变量到CPU缓存中,如果计算机有多个CPU,则线程可能都在不同的CPU被处理,这意味着每个线程都需要将同一个数据复制到不同的CPU Cache中,这样在每个线程都针对同一个变量的数据做了不同的处理后就可能存在数据不一致的情况。具体的多线程读写流程如图所示:

        如果将变量声明为volatile, JVM就能保证每次读取变量时都直接从内存中读取,跳过CPU Cache这一步 ,有效解决了多线程数据同步的问题。具体的流程如图所示: 

        需要说明的是,volatile关键字可以严格保障变量的单次读、写操作的原子性,但并不能保证像i++这种操作的原子性,因为i++在本质上是读、写两次操作。 volatile在某些场景下可以代替synchronized,但是volatile不能完全取代synchronize的位置,只有在一些特殊场景下才适合使用volatile。比如,必须同时满足下面两个条件才能保证并发环境的线程安全。

  • 对变量的写操作不依赖于当前值(比如i++),或者说是单纯的变量赋值( boolean flag = true)。
  • 该变量没有被包含在具有其他的不变式中,也就是说在不同的volatile变量之间不能互相依赖,只有在状态真正独立于程序内的其他内容时才能使用volatile。

        volatile关键字的使用方法比较简单,直接在定义变量时加上volatile关键字即可:

volatile boolean flag = false; 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿德小仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值