当前位置:首页 > 科技  > 软件

并发协调神器CountDownLatch和CyclicBarrier

来源: 责编: 时间:2024-04-02 17:17:50 101观看
导读1.引言从Java的最初版本开始,就可以利用Java来进行多线程编程。正因为Java从最早的版本就支持多线程编程,程序员们才能够利用Java强大的多线程机制来实现并发任务的执行。然而,多线程编程虽然强大,却也带来了一系列潜在的

Fky28资讯网——每日最新资讯28at.com

1.引言

从Java的最初版本开始,就可以利用Java来进行多线程编程。正因为Java从最早的版本就支持多线程编程,程序员们才能够利用Java强大的多线程机制来实现并发任务的执行。然而,多线程编程虽然强大,却也带来了一系列潜在的问题和挑战。Fky28资讯网——每日最新资讯28at.com

假设有一个共享的咖啡机,多个同事在办公室中使用它来冲泡咖啡。这里就存在着典型的多线程问题:Fky28资讯网——每日最新资讯28at.com

  • 竞态条件(Race Condition):多个同事同时按下冲泡按钮,如果没有适当的控制和同步机制,可能会导致咖啡机出现异常行为,比如一次性冲出过多的咖啡或者没有冲出咖啡。
  • 死锁(Deadlock):例如,如果两个同事分别占用了添加水和放置咖啡粉的步骤,但互相等待对方完成,那么咖啡机就无法继续工作了。
  • 数据同步(Data Synchronization):确保每个同事知道何时该添加水、何时该放置咖啡粉,以及何时可以开始冲泡,从而避免出现混乱或资源争夺的情况。

2.并发引出的问题

并发引发问题的根源归咎于三个方面:Fky28资讯网——每日最新资讯28at.com

(1)可见性: CPU缓存引起

可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到。Fky28资讯网——每日最新资讯28at.com

举个简单的例子,看下面这段代码:Fky28资讯网——每日最新资讯28at.com

//线程1执行的代码int i = 0;i = 10; //线程2执行的代码j = i;

在多线程环境下,如果线程1对变量i进行了修改,但由于CPU缓存的存在,这个修改可能并不会立即被其他线程(比如线程2)所看到。这样,在线程2执行j = i;时,它可能读取到的是旧的i的值,而不是线程1修改后的新值。Fky28资讯网——每日最新资讯28at.com

(2)原子性: 分时复用引起

原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 经典的转账问题:比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。 试想一下,如果这2个操作不具备原子性,会造成什么样的后果。假如从账户A减去1000元之后,操作突然中止。然后又从B取出了500元,取出500元之后,再执行 往账户B加上1000元 的操作。这样就会导致账户A虽然减去了1000元,但是账户B没有收到这个转过来的1000元。 所以这2个操作必须要具备原子性才能保证不出现一些意外的问题。Fky28资讯网——每日最新资讯28at.com

(3)有序性: 重排序引起

Fky28资讯网——每日最新资讯28at.com

有序性:即程序执行的顺序按照代码的先后顺序执行。举个简单的例子,看下面这段代码:Fky28资讯网——每日最新资讯28at.com

int i = 0;              boolean flag = false;i = 1;                //语句1  flag = true;          //语句2

上面代码定义了一个int型变量,定义了一个boolean类型变量,然后分别对两个变量进行赋值操作。从代码顺序上看,语句1是在语句2前面的,那么JVM在真正执行这段代码的时候会保证语句1一定会在语句2前面执行吗? 不一定,为什么呢? 这里可能会发生指令重排序。Fky28资讯网——每日最新资讯28at.com

3.CountDownLatch和CyclicBarrier如何让多线程步调一致

在多线程编程中,有时我们需要让多个线程协同工作,以便在某个点上保持步调一致。Java中的CountDownLatch和CyclicBarrier就是用来实现这一目的的两个重要工具。Fky28资讯网——每日最新资讯28at.com

(1)CountDownLatch

实现原理:Fky28资讯网——每日最新资讯28at.com

CountDownLatch是一个计数器,它允许一个或多个线程等待一组操作完成。其基本思想是,一个线程或多个线程在执行任务时,需要等待其他线程都执行完毕后才能继续执行。CountDownLatch通过一个计数器来实现,当计数器为0时,等待的线程可以继续执行。Fky28资讯网——每日最新资讯28at.com

Fky28资讯网——每日最新资讯28at.com

这段代码演示了使用CountDownLatch来实现多个线程之间的协同工作,确保它们按照预期顺序执行。Fky28资讯网——每日最新资讯28at.com

import java.util.concurrent.CountDownLatch;public class Main {    public static void main(String[] args) throws InterruptedException {        CountDownLatch latch = new CountDownLatch(3); // 初始化计数器为3        Runnable task = () -> {            // 执行一些操作            latch.countDown(); // 操作完成后将计数器减1        };        for (int i = 0; i < 3; i++) {            new Thread(task).start(); // 启动3个线程执行任务        }        latch.await(); // 等待计数器变为0        System.out.println("所有操作已完成,多线程步调一致!");    }}

当3个线程都调用了countDown之后,主线程从await返回,并输出"所有操作已完成,多线程步调一致!Fky28资讯网——每日最新资讯28at.com

(2)CyclicBarrier

实现原理:Fky28资讯网——每日最新资讯28at.com

字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。Fky28资讯网——每日最新资讯28at.com

Fky28资讯网——每日最新资讯28at.com

这段代码演示了如何使用CyclicBarrier来实现多个线程在一个屏障点进行等待,并在达到该位置后同时继续执行后续操作。CyclicBarrier常用于多个线程协同工作,并且需要在某个点上实现同步,在实际场景中可以用于控制多线程分阶段执行任务的情况。Fky28资讯网——每日最新资讯28at.com

import java.util.concurrent.CyclicBarrier;public class Main {    public static void main(String[] args) {        CyclicBarrier barrier = new CyclicBarrier(3, () -> {            System.out.println("所有线程已到达栅栏位置,开始执行后续操作!");        });        Runnable task = () -> {            try {                // 执行一些操作                barrier.await(); // 等待其他线程到达栅栏位置            } catch (Exception e) {                e.printStackTrace();            }        };        for (int i = 0; i < 3; i++) {            new Thread(task).start(); // 启动3个线程执行任务        }    }}

4.总结

CountDownLatch 和 CyclicBarrier 都是 Java 多线程编程中用于控制多个线程协同工作的工具,但它们之间有一些关键的区别,用法的区别如下:CountDownLatch 主要用来解决一个线程等待多个线程的场景,可以类比旅游团团长要等待所有的游客到齐才能去下一个景点;而CyclicBarrier 是一组线程之间互相等待,更像是几个驴友之间不离不弃。Fky28资讯网——每日最新资讯28at.com

(1)功能不同:

  • CountDownLatch 用于让一个或多个线程等待其他线程完成操作,达到某个条件后再继续执行。
  • CyclicBarrier 则用于让多个线程在指定位置处进行等待,然后同时继续执行。

(2)重用性:

  • CountDownLatch 是一次性的,一旦计数器变为0,就不能再重置使用。
  • CyclicBarrier 可以被重复使用,当所有线程都到达屏障点后,可以选择重置栅栏来进行下一轮等待。

(3)回调操作:

  • CountDownLatch 没有提供回调操作的机制。
  • CyclicBarrier 可以在所有线程到达屏障点后执行指定的合并操作。

(4)适用场景:

  • CountDownLatch 适用于一组线程需要等待另一组线程执行完毕后再执行的情况,或者等待某些条件满足后再继续执行。
  • CyclicBarrier 适用于多个线程需要在特定点同步,并且在这一点上需要同时继续执行后续操作的情况。

总的来说,CountDownLatch的适用场景更倾向于等待其他线程的任务完成,而CyclicBarrier更适用于多个线程在特定位置同步后继续执行。两者都是非常有用的并发控制工具,能够帮助程序员更加灵活地管理多线程任务的执行顺序。Fky28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-80833-0.html并发协调神器CountDownLatch和CyclicBarrier

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: JSDoc:一个可选的 TypeScript 替代品

下一篇: 一文理解Python的全局解释器锁(GIL)

标签:
  • 热门焦点
Top