深入探索Java并发工具Phaser
在多线程编程中,同步机制是保证线程安全和数据一致性的关键。Java提供了多种同步工具,如CyclicBarrier
和CountDownLatch
,它们在特定场景下非常有用。然而,java.util.concurrent.Phaser
是一个更为强大和灵活的同步工具,它不仅支持重复使用的屏障功能,还允许动态注册和注销参与方,以及在阶段变化时执行自定义操作。
Phaser作为可重复使用的屏障
Phaser
的一个基本用法是作为一个可重复使用的屏障,类似于CyclicBarrier
。以下是一个使用Phaser
实现屏障功能的示例:
public class PhaserExample {
private static final Phaser phaser = new Phaser();
public static void main(String[] args) throws InterruptedException {
startTask(0);
startTask(500);
startTask(1000);
}
private static void startTask(long initialDelay) throws InterruptedException {
Thread.sleep(initialDelay);
new Thread(PhaserExample::taskRun).start();
}
private static void taskRun() {
phaser.register(); // 注册当前线程
print("注册后");
for (int i = 1; i <= 2; i++) {
try {
// 执行一些工作
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 屏障点
print("第" + i + "次到达前");
phaser.arriveAndAwaitAdvance(); // 当前线程将等待其他线程到达
print("第" + i + "次到达后");
}
}
private static void print(String msg) {
// 打印线程状态信息
}
}
在这个示例中,所有线程在执行到arriveAndAwaitAdvance()
方法时会阻塞,直到所有注册的线程都到达屏障点。这确保了所有线程在继续执行之前都已经到达了这一点。
动态屏障点
与CyclicBarrier
和CountDownLatch
不同,Phaser
允许在运行时动态地注册和注销参与方。以下是一个示例,演示如何使用register()
和arriveAndDeregister()
方法来动态改变屏障点:
public class PhaserExample2 {
private static final Phaser phaser = new Phaser();
private static AtomicBoolean unRegisteredFlag = new AtomicBoolean(false);
// ... 省略部分代码
private static void taskRun() {
phaser.register(); // 注册当前线程
print("注册后");
for (int i = 1; i <= 2; i++) {
// ... 省略部分代码
phaser.arriveAndAwaitAdvance(); // 当前线程将等待其他线程到达
print("第" + i + "次到达后");
if(!unRegisteredFlag.get()){
unRegisteredFlag.set(true);
print("注销中");
phaser.arriveAndDeregister(); // 注销参与方
break;
}
}
}
}
在这个示例中,一个参与方在第一次迭代后注销自身,这减少了注册的计数。
构造时的注册计数
Phaser
的构造函数Phaser(int parties)
可以在创建时就注册指定数量的线程/参与方。以下是一个示例,演示如何在构造时注册参与方,并在满足条件时触发屏障:
public class PhaserExample3 {
private static final Phaser phaser = new Phaser(1); // 初始注册一个参与方
// ... 省略部分代码
public static void main(String[] args) throws InterruptedException {
print("主方法执行任务前");
startTask();
startTask();
startTask();
// 执行一些工作
Thread.sleep(10000);
print("主线程注销");
phaser.arriveAndDeregister(); // 注销一个参与方
}
}
在这个示例中,当主线程执行arriveAndDeregister()
方法时,它将注销自身,这将触发屏障并允许其他线程继续执行。
阶段变化时的自定义操作
Phaser
允许通过重写onAdvance
方法在阶段变化时执行自定义操作,并控制是否终止同步。以下是一个示例,演示如何使用onAdvance
方法:
public class PhaserExample4 {
private static final Phaser phaser = new Phaser() {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
print(String.format("阶段变化: phase=%s, registered=%s", phase, registeredParties));
return true; // 可以控制是否终止同步
}
};
// ... 省略部分代码
}
在这个示例中,每次阶段变化时都会打印相关信息,并且同步将被终止。
示例项目
本示例项目使用以下依赖和技术:
- JDK 1.8
- Maven 3.3.9
通过这些示例,我们可以看到Phaser
是一个功能丰富且灵活的同步工具,适用于需要动态同步多个线程的场景。希望这些示例能帮助你更好地理解和使用Phaser
。