掘金 后端 ( ) • 2024-05-19 09:02

在Java并发编程中,线程间的协作与通信是实现复杂并发逻辑的关键。PhaserCyclicBarrier, 和 Semaphore 是Java并发包提供的强大工具,它们分别适用于不同的同步需求。本文将深入浅出地介绍这三个组件的使用场景、常见问题及避免策略,并附上代码示例。

image.png

1. Phaser - 阶段性任务协调器

介绍

Phaser 是一个灵活的同步屏障,支持动态注册和取消注册参与者,适用于有多个阶段的任务执行流程。

常见问题与避免策略

  • 问题:过度依赖动态注册,导致阶段结束条件难以预测。
  • 避免:明确每个阶段的预期参与者数量,适时使用arriveAndDeregister方法。

示例

import java.util.concurrent.Phaser;

public class PhaserDemo {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(1); // 初始参与者为1(主线程)
        
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " arrived.");
                phaser.arriveAndAwaitAdvance(); // 到达并等待所有到达
                System.out.println(Thread.currentThread().getName() + " continuing.");
            }, "Thread-" + (i+1)).start();
        }
        
        phaser.arriveAndDeregister(); // 主线程到达并注销
    }
}

2. CyclicBarrier - 循环屏障

介绍

CyclicBarrier 允许一组线程相互等待,直到达到某个屏障点后一起继续执行。它支持重置和重复使用,适用于循环执行的任务。

常见问题与避免策略

  • 问题:忘记处理BrokenBarrierException,导致程序意外终止。
  • 避免:在run()方法中捕获并适当处理此异常。

示例

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

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        int parties = 3;
        CyclicBarrier barrier = new CyclicBarrier(parties, () -> System.out.println("All threads reached the barrier."));
        
        for (int i = 0; i < parties; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " is waiting on barrier.");
                    barrier.await(); // 等待所有线程到达
                    System.out.println(Thread.currentThread().getName() + " continued.");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, "Thread-" + (i+1)).start();
        }
    }
}

3. Semaphore - 信号量

介绍

Semaphore 是一种计数信号量,用于控制同时访问特定资源的线程数量,常用于限流和资源池管理。

常见问题与避免策略

  • 问题:未正确释放信号量,导致资源泄露。
  • 避免:确保acquirerelease成对出现,即使在异常情况下也要释放。

示例

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2); // 允许两个线程同时访问
        
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    System.out.println(Thread.currentThread().getName() + " entered the critical section.");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " leaving the critical section.");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    semaphore.release(); // 释放许可
                }
            }, "Thread-" + (i+1)).start();
        }
    }
}

总结而言,PhaserCyclicBarrier, 和 Semaphore 分别提供了不同维度的线程间通信和同步机制。理解它们的特性和正确使用,是实现高效并发程序的关键。在实际应用中,应根据具体场景选择合适的工具,并注意异常处理和资源管理,以避免常见的并发陷阱。