将线程注册到Phaser

时间:2018-11-26 03:19:37

标签: java multithreading concurrency java.util.concurrent phaser

我正在了解Phaser。这样做时,我遇到了一个问题。以下是我拥有的代码

public class RunnableTask implements Runnable {

    private Phaser phaser;

    public RunnableTask(Phaser phaser) {
        this.phaser = phaser;
        this.phaser.register();  // Question
    }

    @Override
    public void run() {
        // this.phaser.register();  // Question
        print("After register");
        for (int i = 0; i < 2; i++) {
            sleep();
            print("Before await" + i + ":");
            this.phaser.arriveAndAwaitAdvance();
            print("After advance" + i + ":");
        }
    }

    private void sleep() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void print(String msg) {

        System.out.println(String.format("%s: %s, time=%s, registered=%s, arrived=%s, unarrived=%s, phase=%s.", msg,
                Thread.currentThread().getName(), LocalTime.now(), this.phaser.getRegisteredParties(),
                this.phaser.getArrivedParties(), this.phaser.getUnarrivedParties(), this.phaser.getPhase()));
    }

 }

上面的示例测试

public class TestPhaser {

    public static void main(String[] args) {

        Phaser phaser = new Phaser();

        RunnableTask task = new RunnableTask(phaser);

        Thread t1 = new Thread(task, "t1");
        Thread t2 = new Thread(task, "t2");
        Thread t3 = new Thread(task, "t3");

        t1.start();
        t2.start();
        t3.start();
    } 
}

执行上述程序后,输出为:

  

注册后:t3,时间= 22:01:26.636,注册= 1,到达= 0,   未到达= 1,相位= 0。

     

注册后:t2,时间= 22:01:26.636,注册= 1,到达= 0,   未到达= 1,相位= 0。

     

注册后:t1,时间= 22:01:26.636,注册= 1,到达= 0,   未到达= 1,相位= 0。

     

在等待0 :: t3之前,时间= 22:01:28.728,注册= 1,到达= 0,   未到达= 1,相位= 0。

     

在等待0 :: t2之前,时间= 22:01:28.728,注册= 1,到达= 0,   未到达= 1,相位= 0。

     

在等待0 :: t1之前,时间= 22:01:28.728,注册= 1,到达= 0,   未到达= 1,相位= 0。

     

提前0 :: t1之后,时间= 22:01:28.728,注册= 1,到达= 0,   未到达= 1,相位= 3。

     

提前0 :: t2,时间= 22:01:28.728,注册= 1,到达= 0,   未到达= 1,相位= 3。

     

提前0 :: t3之后,时间= 22:01:28.729,注册= 1,到达= 0,   未到达= 1,相位= 3。

     

在等待1 :: t2之前,时间= 22:01:30.730,已注册= 1,到达= 0,   未到达= 1,相位= 3。

     

在等待1 :: t3之前,时间= 22:01:30.730,已注册= 1,到达= 0,   未到达= 1,相位= 3。

     

提前1 :: t2之后,时间= 22:01:30.730,注册= 1,到达= 0,   未到达= 1,相位= 4。

     

前进1 :: t3之后,时间= 22:01:30.732,注册= 1,到达= 0,   未到达= 1,相位= 5。

     

在等待1 :: t1之前,时间= 22:01:30.730,已注册= 1,到达= 0,   未到达= 1,相位= 3。

     

提前1 :: t1之后,时间= 22:01:30.732,注册= 1,到达= 0,   未到达= 1,相位= 6。

您会发现这里有很多差异。线程不按顺序前进。此外,几乎没有几个阶段或/和没有一个阶段。

当我将代码行this.phaser.register()从构造函数移动到run方法的开头时,输出为:

  

注册后:t1,时间= 22:10:58.230,注册= 3,到达= 0,   未到达= 3,相位= 0。

     

注册后:t3,时间= 22:10:58.230,注册= 3,到达= 0,   未到达= 3,相位= 0。

     

注册后:t2,时间= 22:10:58.230,注册= 3,到达= 0,   未到达= 3,相位= 0。

     

在等待0 :: t2之前,时间= 22:11:00.314,已注册= 3,到达= 0,   未到达= 3,相位= 0。

     

在等待0 :: t1之前,时间= 22:11:00.314,已注册= 3,到达= 0,   未到达= 3,相位= 0。

     

在等待0 :: t3之前,时间= 22:11:00.314,已注册= 3,到达= 0,   未到达= 3,相位= 0。

     

提前0 :: t2之后,时间= 22:11:00.315,注册= 3,到达= 0,   未到达= 3,阶段= 1。

     

提前0 :: t3之后,时间= 22:11:00.315,注册= 3,到达= 0,   未到达= 3,阶段= 1。

     

提前0 :: t1,时间= 22:11:00.315,注册= 3,到达= 0,   未到达= 3,阶段= 1。

     

在等待1 :: t1之前,时间= 22:11:02.319,已注册= 3,到达= 0,   未到达= 3,阶段= 1。

     

在等待1 :: t2之前,时间= 22:11:02.319,已注册= 3,到达= 0,   未到达= 3,阶段= 1。

     

在等待1 :: t3之前,时间= 22:11:02.319,已注册= 3,到达= 0,   未到达= 3,阶段= 1。

     

提前1 :: t3之后,时间= 22:11:02.320,已注册= 3,到达= 0,   未到达= 3,阶段= 2。

     

提前1 :: t2之后,时间= 22:11:02.320,注册= 3,到达= 0,   未到达= 3,阶段= 2。

     

提前1 :: t1之后,时间= 22:11:02.321,注册= 3,到达= 0,   未到达= 3,阶段= 2。

这看起来好得多,因为线程的执行和阶段是按顺序进行的。

这是我的问题:

1)当各方在Runnable的构造函数中注册时,为什么会有很多差异?

2)在第二个结果中,到达和未到达的状态在每个阶段均为零(不正确)。那么,如何为他们获取正确的数字?

感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

在第一个示例“构造器中的creation phaser”中,您仅向Phaser注册一个线程。您必须创建三个任务,在相位器中注册三个线程。

像这样更改代码,它将起作用。 (不要忘记从代码中删除初始的RunnableTask任务= new RunnableTask(phaser);

    Thread t1 = new Thread(new RunnableTask(phaser), "t1");
    Thread t2 = new Thread(new RunnableTask(phaser), "t2");
    Thread t3 = new Thread(new RunnableTask(phaser), "t3");

在第二个示例中,您在所有线程中恰好等待了2秒钟,这是准确的,所有线程几乎都同时到达并等待,像这样更改您的sleep方法以引入一些不同的等待线程来查看线程到达和未到达的线程

  private void sleep() {
    try {
      Random r = new Random();    
      TimeUnit.SECONDS.sleep(r.nextInt(5));
    } catch(InterruptedException e) {
      e.printStackTrace();
    }
  }

您的第二个示例有效,但它不正确,它仅适用于您在run方法开始时处于睡眠状态,因此所有线程在您调用phaser上的advance和前进方法之前都赶上了注册。 如果您要删除睡眠,请在致电此行之后

t1.start();

将运行T1运行方法,并在移相器中注册t1线程。然后可能在线程t2和t3启动并在相位器中注册之前,将调用t1 run方法中的 this.phaser.arriveAndAwaitAdvance(),这样相位器就不会等待它们。

您应该在任务的构造函数中或在启动线程之前调用的方法中注册到相位器中。