常用的同步辅助类

CountDownLatch

CountDownLatch 的基本使用

CountDownLatch 通过一个计数器来跟踪某些操作的完成次数 。线程会被阻塞直到计数器变为零 。一般来说,CountDownLatch 用于以下场景:

  • 等待多个线程执行完成:例如,等待多个子线程完成任务后,主线程再继续执行。
  • 并行任务的同步:确保一些任务在所有子任务完成之后才继续执行。
csharp 复制代码
public class CountDownLatchTest {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "执行完毕");
                countDownLatch.countDown();
            }, "t" + i).start();
        }
        System.out.println("等待所有线程执行完毕");
    }
}

CountDownLatch ****的关键方法

await() 使当前线程等待 ,直到计数器的值为零。该方法是阻塞的,线程会在此方法处被挂起,直到计数器的值减为 0。

CountDownLatch 的应用场景

  1. 等待多个线程完成任务
    • 比如,在一个任务分解为多个子任务的场景中,我们希望等所有的子任务完成后,再继续进行下一个步骤。CountDownLatch 就可以用来保证主线程等到所有子线程的任务完成后再继续执行。
  1. 并行任务的同步控制
    • 比如,某些操作必须等到多个任务执行完成后才能继续,CountDownLatch 可以确保这些任务完成后才继续处理。
  1. 启动多个线程并等待其完成
    • 比如,在并行处理的情况下,启动多个线程并在主线程中等待所有线程的完成,可以通过 CountDownLatch 来实现。

总之就是在某些场景下,必须等待一些线程完成后才能继续进行就可以使用这个 CountDownLatch****


CyclicBarrier

CyclicBarrier 的基本原理

CyclicBarrier 使得多个线程在执行某个任务时,必须等待其他线程到达屏障点。每当所有线程都到达屏障点时,CyclicBarrier 会让这些线程继续执行。可以理解为它提供了一种线程间的协作方式,多个线程在到达同步点时会被阻塞,直到所有线程都到达这个点。

CountDownLatch 不同,CyclicBarrier可重用 的。CountDownLatch 一旦计数器变为零后就不能重用,而 CyclicBarrier 在每次屏障点通过后,可以重置为初始状态,允许进行下一轮的等待。

CyclicBarrier 的关键方法

await() :每个线程调用此方法,表示该线程已经到达了屏障点并且等待其他线程的到达。调用 await() 方法的线程会被阻塞,直到所有线程都到达屏障点。

csharp 复制代码
public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("集齐7颗龙珠,召唤神龙");
        });
        // 创建7个线程模拟收集龙珠
        for (int i = 1; i <= 7; i++) {
            final int tempInt = i;
            new Thread(() -> {
                try {
                    System.out.println("收集到第" + tempInt + "颗龙珠");
                    // 每个线程到达屏障时调用await
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

CyclicBarrier 的应用场景

  • 并行任务的同步 :比如你有多个线程并行执行任务,每个线程执行完任务后都需要等待其他线程完成某些操作再继续,CyclicBarrier 可以用来在这些线程之间同步操作。
  • 分批执行的任务:多个任务被分成多个子任务并行处理,每次等待所有子任务完成后再进行下一轮操作。
  • 分阶段执行 :比如某个计算任务分为多个阶段,每个阶段都需要多个线程协作,CyclicBarrier 可以确保每个阶段的任务都在所有线程完成后同步进行

在这种情况下会发生死锁

总结

  • CyclicBarrier 是一个同步工具类,允许多个线程在执行任务时相互等待,直到所有线程都到达屏障点后才继续执行。
  • 它是 可重用 的,并且适用于需要线程间在多个阶段同步执行的场景。
  • 使用 CyclicBarrier 时需要注意可能的死锁问题,以及适当处理线程中断和屏障破坏的异常。

Semaphore

Semaphore 的基本原理

Semaphore 维护了一个许可证计数器。每当一个线程请求资源时,它尝试从 Semaphore 中获取一个许可证,如果计数器大于 0,则获取一个许可证并将计数器减 1;否则线程会被阻塞,直到有许可证可用。释放资源时,线程释放许可证,计数器加 1,其他等待的线程可以继续执行。

Semaphore 的关键方法

acquire() :请求一个许可证。如果没有可用的许可证,当前线程会阻塞直到有许可证可用。

release() :释放一个许可证,使得其他等待的线程能够获得许可。如果有线程在等待许可证,release() 会唤醒其中一个线程。

Semaphore 应用场景

  • 限制并发线程数:通过限制某个资源的并发访问数量。例如,限制数据库连接池中最大连接数,或者限制网络带宽的并发访问。
  • 访问控制:控制对某些资源(如文件、设备等)的并发访问,确保同时访问资源的线程数不会超过设定的阈值。
  • 限流控制 :在 Web 服务或 API 中,使用 Semaphore 可以实现对请求的并发数限制,从而防止服务器被过多请求压垮。

总结

一个加法计数器 一个减法计数器 一个信号量

特性 Semaphore CountDownLatch CyclicBarrier
主要功能 控制并发线程数,管理资源池 等待多个线程执行完毕,或者多个线程完成后继续执行 等待多个线程到达屏障点后继续执行
可重用性 可重用:允许多次获取和释放许可证 不可重用:计数器归零后不能重置 可重用:每次所有线程到达屏障后,可以重用
适用场景 控制并发访问共享资源 等待多个线程完成后统一执行某些操作 多阶段任务的同步,多个线程在不同阶段同步
阻塞与唤醒 线程通过 acquire() 获取许可,release() 释放许可 线程通过 await() 等待,countDown() 减少计数器 线程通过 await() 等待,所有线程到达后继续执行
常见用途 资源池管理,流量控制,限流 并行任务同步,启动多个线程后等待它们完成 分阶段同步,线程池等同步任务
相关推荐
程序猿小蒜2 小时前
基于springboot的车辆管理系统设计与实现
java·数据库·spring boot·后端·spring·oracle
90后的晨仔2 小时前
Java后端开发:从零构建企业级应用的完整架构与技术栈详解
后端
我命由我123453 小时前
Spring Cloud - Spring Cloud 声明式接口调用(Fiegn 声明式接口调用概述、Fiegn 使用)
java·后端·spring·spring cloud·微服务·架构·java-ee
canonical_entropy3 小时前
领域驱动设计(DDD)中聚合根的最主要职责真的是维护一致性吗?
后端·架构·领域驱动设计
AntBlack3 小时前
不当韭菜 : 好像真有点效果 ,想藏起来自己用了
前端·后端·python
橙子家4 小时前
Serilog 日志库的简介
后端
间彧4 小时前
Java线程池深度实战:不同场景下的最优选择与性能优化
后端
间彧4 小时前
CompletableFuture与线程池:并发编程的双剑合璧
后端
间彧4 小时前
在实际项目中,如何根据任务类型(CPU/IO密集型)设计不同的线程池策略?
后端
golang学习记4 小时前
Go slog 日志打印最佳实践指南
开发语言·后端·golang