Skip to content

Instantly share code, notes, and snippets.

@sunwu51
Last active March 20, 2023 13:38
Show Gist options
  • Save sunwu51/0cc15f42c6923a1498d869a85b6d905b to your computer and use it in GitHub Desktop.
Save sunwu51/0cc15f42c6923a1498d869a85b6d905b to your computer and use it in GitHub Desktop.
phaser题目

题目

有4线程,分阶段执行任务,第0阶段,需4个都执行完才能开始下一阶段。第1阶段,需3个都执行完才能开始下一阶段。第2阶段,需2个都执行完才算最终完成。

public class Main{
    public static void main(String[] args) {
        test();
    }
    static void test(){
        Phaser phaser = new Phaser(4);
        for(int i=0;i<4;i++){
            new Thread(()->{
                sleep(3);
                exec(0);
                arrive(phaser, 0);
                phaser.awaitAdvance(0);

                sleep(30);
                exec(1);
                arrive(phaser, 1);
                phaser.awaitAdvance(1);// 注释: 如果当前phase不等于1,则这句就不会阻塞,视频里讲过

                sleep(3);
                exec(2);
                arrive(phaser, 2);
                phaser.awaitAdvance(2);
            }).start();
        }
        phaser.awaitAdvance(0);
        System.out.println("阶段0完成");
        phaser.arriveAndDeregister();  // 注释:arriveAndDeregister的作用是总数-1(未完成数-1 )
        phaser.awaitAdvance(1);
        System.out.println("阶段1完成");
        phaser.arriveAndDeregister();
        phaser.awaitAdvance(2);
        System.out.println("阶段2完成即完成");
    }

    static synchronized void arrive(Phaser phaser, int phase){
        if(phaser.getPhase() == phase){
            phaser.arrive();
        }
    }
    static void sleep(int i){
        try {
            Thread.sleep(new Double(Math.random()*1000*i).longValue());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static void exec(int x){
        System.out.println(Thread.currentThread().getName()+"执行任务"+x);
    }
}
@sunwu51
Copy link
Author

sunwu51 commented Dec 30, 2019

利用了awaitAdvance(n)这个函数的特性:如果当前不是第n阶段,则这句不会阻塞,而是直接返回。即相当于空语句。

@sunwu51
Copy link
Author

sunwu51 commented Feb 1, 2020

主线程arriveAndDeregister这句有的人会觉得,如果第1阶段开始后,所有线程瞬间执行完毕,然后才执行到这句,岂不是第一阶段4个线程都完成了才去注销。会有问题吗?

其实不会,如果线程执行过快,会导致直接进了第2阶段,第2阶段注销两次,并没有问题。一定还是保证了"阶段1完成"打印的时候,至少3个线程执行完了阶段1,阶段2同样。

实在理解不了,就要卡这个数目的可以参考如下代码:

public class Main{
    public static void main(String[] args) {
        test();
    }
    static void test(){
        Phaser phaser = new Phaser(4);
        for(int i=0;i<4;i++){
            new Thread(()->{
                sleep(3);
                exec(0);
                arrive(phaser, 0);
                phaser.awaitAdvance(0);

                sleep(3);
                exec(1);
                arrive(phaser, 1);
                phaser.awaitAdvance(1);// 注释: 如果当前phase不等于1,则这句就不会阻塞,视频里讲过

                sleep(30);
                exec(2);
                arrive(phaser, 2);
                phaser.awaitAdvance(2);
            }).start();
        }
        phaser.awaitAdvance(0);
        System.out.println("阶段0完成");
        phaser.awaitAdvance(1);
        System.out.println("阶段1完成");
        phaser.awaitAdvance(2);
        System.out.println("阶段2完成即完成");
    }
    static boolean[] firstArrive = new boolean[]{true,true,true};

    static synchronized void arrive(Phaser phaser, int phase){
        if(phaser.getPhase() == phase){
            if(phase>0 && firstArrive[phase]){
                phaser.arriveAndDeregister();  // 注释:arriveAndDeregister的作用是总数-1(未完成数-1 )
                firstArrive[phase] = false;
            }
            phaser.arrive();
        }
    }
    static void sleep(int i){
        try {
            Thread.sleep(new Double(Math.random()*1000*i).longValue());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static void exec(int x){
        System.out.println(Thread.currentThread().getName()+"执行任务"+x);
    }
}

@donnieYeh
Copy link

从b站过来的,实际上在主线程里多调一次arrive还是有问题的,在a线程释放屏障准备进入下一阶段的过程中,b线程调用arrive,就会有冲突。把睡眠时间去掉,循环test ()100次就可以复现该问题,目前我也没想到什么好方法呢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment