Java中使用Phaser实现线程同步的错误用法排查
Phaser实现线程顺序同步的错误用法分析
测试代码
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FooRunner { public static void main(String[] args) { Foo foo = new Foo(); ExecutorService executors = Executors.newFixedThreadPool(3); Runnable first = () -> { try { foo.first(() -> System.out.println("first")); } catch (InterruptedException e) { throw new RuntimeException(e); } }; Runnable second = () -> { try { foo.second(() -> System.out.println("second")); } catch (InterruptedException e) { throw new RuntimeException(e); } }; Runnable third = () -> { try { foo.third(() -> System.out.println("third")); } catch (InterruptedException e) { throw new RuntimeException(e); } }; executors.submit(third); executors.submit(first); executors.submit(second); executors.shutdown(); } }
基于Phaser的同步实现代码
import java.util.concurrent.Phaser; public class Foo { private final Phaser phaser = new Phaser(1); public Foo() {} public void first(Runnable printFirst) throws InterruptedException { printFirst.run(); phaser.arriveAndAwaitAdvance(); } public void second(Runnable printSecond) throws InterruptedException { phaser.awaitAdvance(1); // 等待first完成 printSecond.run(); phaser.arriveAndAwaitAdvance(); } public void third(Runnable printThird) throws InterruptedException { phaser.awaitAdvance(2); // 等待second完成 printThird.run(); phaser.arriveAndAwaitAdvance(); } }
预期输出
first second third
问题
但实际线程并未在Phaser上等待,请问当前的Phaser实现是否属于错误用法?
回答
当前的Phaser实现确实是错误用法,核心问题在于对Phaser的阶段同步逻辑理解有误:
阶段推进逻辑不匹配顺序同步需求
Phaser初始化时指定了1个参与者,first方法调用arriveAndAwaitAdvance()时,仅有的1个参与者已到达,阶段会直接从0推进到1,方法立即返回,不会等待任何其他线程。这导致first执行后没有阻塞,无法保证后续线程的执行顺序。awaitAdvance方法使用逻辑错误
awaitAdvance(int phase)的作用是等待Phaser的阶段号大于传入值,若当前阶段号已满足条件则直接返回:- 若
first先执行完成,阶段号变为1,second中的awaitAdvance(1)会直接返回,不会等待; - 若
second先执行,阶段号还是0,awaitAdvance(1)会进入等待,但这只是偶然阻塞,并非可靠的顺序保证; third中的awaitAdvance(2)同理,依赖阶段号的方式无法稳定实现线程顺序执行。
- 若
另外需要明确:Phaser并非实现线程顺序同步的合适选择,它的设计目标是支持多线程分阶段协同工作(所有线程完成当前阶段后,再一起进入下一阶段),而非保证线程按固定顺序依次执行。
Phaser典型使用示例
Phaser phaser = new Phaser(4); // 3个工作线程 + 主线程作为参与者 for (int i = 0; i < 3; i++) { new Thread( () -> { try { System.out.println( "线程" + Thread.currentThread().getName() + "等待进入第一阶段"); phaser.arriveAndAwaitAdvance(); // 等待所有参与者到达第一阶段 Thread.sleep(Duration.ofSeconds(10)); System.out.println("线程" + Thread.currentThread().getName() + "准备进入第二阶段"); // 第二阶段 phaser.arriveAndAwaitAdvance(); // 等待所有参与者到达第二阶段 } catch (Exception e) { e.printStackTrace(); } }) .start(); } phaser.arriveAndDeregister(); // 主线程完成任务,注销自己作为参与者
内容的提问来源于stack exchange,提问作者sinsuren
相关产品推荐
相关产品推荐

