Java 并发包中的同步工具类(Synchronizers)使用详解

发布于:2025-07-24 ⋅ 阅读:(21) ⋅ 点赞:(0)

前言

在 Java 多线程编程中,线程之间的协调和同步是非常关键的部分。为了简化线程间的协作,Java 5 引入了 java.util.concurrent 包,其中包含了多个同步工具类(Synchronizers),这些工具类可以帮助我们更方便地控制线程的执行顺序、等待条件和资源访问。

本文将详细介绍 java.util.concurrent 包中常用的同步工具类,包括:

  • CountDownLatch
  • CyclicBarrier
  • Phaser
  • Semaphore
  • Exchanger

我们将通过示例代码详细说明它们的使用方法、适用场景以及与 synchronizedReentrantLock 的区别。


一、CountDownLatch(倒计时门闩)

简介

CountDownLatch 是一个同步辅助类,它允许一个或多个线程等待,直到其他线程完成一组操作。它内部维护一个计数器,当计数器减到 0 时,所有等待的线程被释放。

构造函数

public CountDownLatch(int count)

核心方法

  • await():当前线程等待,直到计数器减到 0。
  • countDown():将计数器减 1。

使用场景

  • 启动多个线程后,主线程等待所有线程完成任务。
  • 初始化完成后,再开始执行后续操作。

示例代码

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 3;
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 1; i <= threadCount; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 正在处理任务...");
                    Thread.sleep(2000); // 模拟耗时任务
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown(); // 任务完成,计数器减一
                }
            }, "Thread-" + i).start();
        }

        System.out.println("主线程等待所有子线程完成...");
        latch.await(); // 主线程在此阻塞,直到计数器为0
        System.out.println("所有子线程已完成,主线程继续执行...");
    }
}

输出示例

主线程等待所有子线程完成...
Thread-1 正在处理任务...
Thread-2 正在处理任务...
Thread-3 正在处理任务...
所有子线程已完成,主线程继续执行...

二、CyclicBarrier(循环屏障)

简介

CyclicBarrier 允许一组线程互相等待,直到所有线程都到达某个公共屏障点(barrier point)。与 CountDownLatch 不同的是,CyclicBarrier 是可以重用的。

构造函数

public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)

核心方法

  • await():线程调用此方法表示到达屏障点,进入等待,直到所有线程都到达。

使用场景

  • 多线程协同执行任务,每轮任务结束后进行汇总或处理。
  • 游戏中多个玩家准备就绪后开始游戏。

示例代码

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int threadCount = 3;
        CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
            System.out.println("所有线程已到达屏障点,开始汇总任务...");
        });

        for (int i = 1; i <= threadCount; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 正在执行任务...");
                    Thread.sleep((long) (Math.random() * 2000));
                    System.out.println(Thread.currentThread().getName() + " 到达屏障点");
                    barrier.await(); // 等待其他线程
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, "Thread-" + i).start();
        }
    }
}

输出示例

Thread-1 正在执行任务...
Thread-2 正在执行任务...
Thread-3 正在执行任务...
Thread-3 到达屏障点
Thread-1 到达屏障点
Thread-2 到达屏障点
所有线程已到达屏障点,开始汇总任务...

三、Phaser(阶段同步器)

简介

Phaser 是 Java 7 引入的一个更灵活的同步工具,它支持动态注册线程、分阶段同步等特性,功能比 CountDownLatchCyclicBarrier 更强大。

核心概念

  • Phase(阶段):每个阶段可以有多个线程参与。
  • Arrive(到达):线程到达一个阶段。
  • Advance(推进):所有线程到达后自动进入下一阶段。

核心方法

  • register():注册一个线程。
  • arrive():线程到达当前阶段。
  • arriveAndAwaitAdvance():线程到达并等待其他线程推进到下一阶段。
  • arriveAndDeregister():到达并注销线程。

使用场景

  • 多阶段任务的协同执行。
  • 动态添加线程的场景。

示例代码

import java.util.concurrent.Phaser;

public class PhaserExample {
    public static void main(String[] args) {
        Phaser phaser = new Phaser();
        phaser.register(); // 主线程注册

        for (int i = 1; i <= 3; i++) {
            phaser.register(); // 每个线程注册
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 开始阶段 0");
                phaser.arriveAndAwaitAdvance(); // 等待所有线程到达

                System.out.println(Thread.currentThread().getName() + " 开始阶段 1");
                phaser.arriveAndAwaitAdvance();

                System.out.println(Thread.currentThread().getName() + " 开始阶段 2");
                phaser.arriveAndDeregister(); // 完成任务并注销
            }, "Thread-" + i).start();
        }

        phaser.arriveAndAwaitAdvance(); // 主线程也参与阶段同步
        System.out.println("所有线程完成阶段 0");

        phaser.arriveAndAwaitAdvance();
        System.out.println("所有线程完成阶段 1");

        phaser.arriveAndDeregister(); // 主线程注销
        System.out.println("Phaser 完成");
    }
}

四、Semaphore(信号量)

简介

Semaphore 控制同时访问的线程数量,可以用来实现资源池、连接池、限流等机制。它维护一组许可(permits),线程通过 acquire() 获取许可,通过 release() 释放许可。

构造函数

public Semaphore(int permits)
public Semaphore(int permits, boolean fair)

核心方法

  • acquire():获取一个许可,如果没有许可可用,线程将阻塞。
  • release():释放一个许可。

使用场景

  • 控制资源并发访问数量。
  • 实现限流、流量控制。

示例代码

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        int permits = 2;
        Semaphore semaphore = new Semaphore(permits);

        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 等待获取许可...");
                    semaphore.acquire(); // 获取许可
                    System.out.println(Thread.currentThread().getName() + " 获取许可,开始执行任务...");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + " 释放许可");
                    semaphore.release(); // 释放许可
                }
            }, "Thread-" + i).start();
        }
    }
}

输出示例

Thread-1 等待获取许可...
Thread-1 获取许可,开始执行任务...
Thread-2 等待获取许可...
Thread-2 获取许可,开始执行任务...
Thread-3 等待获取许可...
Thread-4 等待获取许可...
Thread-5 等待获取许可...
Thread-1 释放许可
Thread-3 获取许可,开始执行任务...
Thread-2 释放许可
Thread-4 获取许可,开始执行任务...
Thread-3 释放许可
Thread-5 获取许可,开始执行任务...
Thread-4 释放许可
Thread-5 释放许可

五、Exchanger(线程交换器)

简介

Exchanger 是一个用于两个线程之间交换数据的同步工具。它提供了一个同步点,在这个点上两个线程可以交换数据。

核心方法

  • exchange(V data):线程在此方法调用时阻塞,直到另一个线程也调用该方法,两者交换数据。

使用场景

  • 线程间数据交换。
  • 双缓冲队列实现。

示例代码

import java.util.concurrent.Exchanger;

public class ExchangerExample {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();

        new Thread(() -> {
            String data = "来自线程 A 的数据";
            System.out.println("线程 A 发送数据: " + data);
            try {
                String response = exchanger.exchange(data);
                System.out.println("线程 A 收到响应: " + response);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "Thread-A").start();

        new Thread(() -> {
            String data = "来自线程 B 的数据";
            System.out.println("线程 B 发送数据: " + data);
            try {
                String response = exchanger.exchange(data);
                System.out.println("线程 B 收到响应: " + response);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "Thread-B").start();
    }
}

输出示例

线程 A 发送数据: 来自线程 A 的数据
线程 B 发送数据: 来自线程 B 的数据
线程 B 收到响应: 来自线程 A 的数据
线程 A 收到响应: 来自线程 B 的数据

六、总结对比

工具类 用途 可重用 特点
CountDownLatch 等待一组线程完成 不可重用 一次性使用
CyclicBarrier 线程相互等待,可重复使用 可重用 可指定屏障动作
Phaser 多阶段同步,动态注册线程 可重用 使用方式更灵活
Semaphore 控制并发访问数量 可重用 支持公平与非公平模式
Exchanger 两个线程之间交换数据 可重用 实现简单且高效

七、与 synchronized 和 ReentrantLock 的区别

对比项 synchronized ReentrantLock 同步工具类
使用方式 使用关键字实现 需要显式加锁和释放 作为辅助类使用
可重入性 支持 支持 依具体实现而定
超时控制 不支持 支持 支持
尝试获取锁 不支持 支持 支持
多线程协作 不支持 不支持 支持

八、结语

Java 并发包中的同步工具类极大地简化了多线程程序的开发,每种工具都有其适用的场景和优势。在实际开发中,应根据业务需求选择合适的同步机制,避免使用不当导致的死锁、资源竞争等问题。

希望本篇博客能帮助你更好地理解和使用 Java 中的同步工具类!如果你有任何问题或建议,欢迎留言讨论。


网站公告

今日签到

点亮在社区的每一天
去签到