【詳解】JUC之Phaser(移相器)

簡介

java7中引入了一種新的可重複使用的同步屏障,稱爲移相器Phaser。Phaser擁有與CyclicBarrierCountDownLatch類似的功勞.

但是這個類提供了更加靈活的應用。CountDownLatch和CyclicBarrier都是隻適用於固定數量的參與者。移相器適用於可變數目的屏障,在這個意義上,可以在任何時間註冊新的參與者。並且在抵達屏障是可以註銷已經註冊的參與者。因此,註冊到同步移相器的參與者的數目可能會隨着時間的推移而變化。

如CyclicBarrier一樣,移相器可以重複使用,這意味着當前參與者到達移相器後,可以再一次註冊自己並等待另一次到達.

移相器的另一個重要特徵是:移相器可能是分層的,這允許你以樹形結構來安排移相器以減少競爭

簡單的使用

public class PhaserTest {

    private final static Random RANDOM = new Random();

    public static void main(String[] args) {
        final Phaser phaser = new Phaser();
        IntStream.rangeClosed(1,5).boxed().map(i->phaser).forEach(Task::new);

        phaser.register();

        phaser.arriveAndAwaitAdvance();//相當於CountDown
        System.out.println("All of work are finished.");
    }

    static class Task extends Thread{
        private final Phaser phaser;

        Task(Phaser phaser) {
            this.phaser = phaser;
            phaser.register();//把自己加入計數器中
            start();
        }

        @Override
        public void run() {
            System.out.println("The worker[ "+getName()+ " ]" +" is working.");
            try {
                TimeUnit.SECONDS.sleep(RANDOM.nextInt(5));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            phaser.arriveAndAwaitAdvance();//自己完成,等待其他線程完成,相當於CyclicBarrier
        }
    }
}

結果:

The worker[ Thread-1 ] is working.
The worker[ Thread-2 ] is working.
The worker[ Thread-0 ] is working.
The worker[ Thread-4 ] is working.
The worker[ Thread-3 ] is working.
All of work are finished.

重複使用的例子

public class PhaserTest {

    private final static Random RANDOM = new Random();


    public static void main(String[] args) {
        final Phaser phaser = new Phaser(5);

        for (int i = 0; i < 6; i++) {
            new Athletes(i,phaser).start();
        }
    }

    static class Athletes extends Thread {
        private final int no;
        private final Phaser phaser;


        Athletes(int no, Phaser phaser) {
            this.no = no;
            this.phaser = phaser;

        }

        @Override
        public void run() {
            try {
                System.out.println(no + " start running.");
                TimeUnit.SECONDS.sleep(RANDOM.nextInt(5));
                System.out.println(no + " end running.");
                phaser.arriveAndAwaitAdvance();

                System.out.println(no + " start bicycle.");
                TimeUnit.SECONDS.sleep(RANDOM.nextInt(5));
                System.out.println(no + " end bicycle.");
                phaser.arriveAndAwaitAdvance();


                System.out.println(no + " start long jump.");
                TimeUnit.SECONDS.sleep(RANDOM.nextInt(5));
                System.out.println(no + " end long jump.");
                phaser.arriveAndAwaitAdvance();

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

結果

0 start running.
1 start running.
4 start running.
2 start running.
3 start running.
5 start running.
5 end running.
4 end running.
2 end running.
1 end running.
0 end running.
4 start bicycle.
......

動態減少

static class InjuredAthletes extends Thread {
    private final int no;
    private final Phaser phaser;


    InjuredAthletes(int no, Phaser phaser) {
        this.no = no;
        this.phaser = phaser;

    }

    @Override
    public void run() {
        try {
            sport(no, phaser, " start running.", " end running.");
            sport(no, phaser, " start bicycle.", " end bicycle.");
            System.out.println("I am injured.");
            phaser.arriveAndDeregister();//動態減少
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

static class Athletes extends Thread {
    private final int no;
    private final Phaser phaser;


    Athletes(int no, Phaser phaser) {
        this.no = no;
        this.phaser = phaser;

    }

    @Override
    public void run() {
        try {
            sport(no, phaser, " start running.", " end running.");
            sport(no, phaser, " start bicycle.", " end bicycle.");
            sport(no, phaser, " start long jump.", " end long jump.");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

private static void sport(int no, Phaser phaser, String s, String s2) throws InterruptedException {
    System.out.println(no + s);
    TimeUnit.SECONDS.sleep(RANDOM.nextInt(5));
    System.out.println(no + s2);
    phaser.arriveAndAwaitAdvance();
}

API

重要API

註冊

public int register()
public int bulkRegister(int parties)

register

  • 是註冊一個線程數,比較常用

bulkRegister

  • 可以批量註冊

到達

public int arrive()
public int arriveAndDeregister()
public int arriveAndAwaitAdvance()

arrive

  • 這個到達後,不會阻塞,相當於countdown機制

arriveAndAwaitAdvance

  • 到達後會阻塞,相當於CyclicBarrier機制

arriveAndDeregister

  • 當線程出現異常,不能正常到達時,可以調用該方法,動態減少註冊數

舉例

public class PhaserTest {

    private static final Random RANDOM = new Random();

    public static void main(String[] args) throws InterruptedException {
        final Phaser phaser = new Phaser(5);

        for (int i = 0; i < 4; i++) {
            new ArriveTask(i,phaser).start();
        }
        //等待全部任務進行完成
        phaser.arriveAndAwaitAdvance();
        System.out.println("The phase 1 work finish done.");

    }

    private static class ArriveTask extends Thread{
        private final Phaser phaser;

        private ArriveTask(int no,Phaser phaser) {
            super(String.valueOf(no));

            this.phaser = phaser;
        }

        @Override
        public void run() {
            System.out.println(getName() +  " start working. ");
            threadSleep();
            System.out.println(getName() + " The phase one is running.");
            phaser.arrive();

            threadSleep();
            System.out.println(getName() +  " keep to other thing. ");

        }
    }

    private static void threadSleep()  {
        try {
            TimeUnit.SECONDS.sleep(RANDOM.nextInt(5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

不再等待機制

protected boolean onAdvance(int phase, int registeredParties)

舉例

public class PhaserTest {
    public static void main(String[] args) throws InterruptedException {
        final Phaser phaser = new Phaser(2){
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {

                return true;
            }
        };

        new OnAdvanceTask("Alex",phaser).start();
        new OnAdvanceTask("Jack",phaser).start();

        TimeUnit.SECONDS.sleep(3);
        System.out.println(phaser.getArrivedParties());
        System.out.println(phaser.getUnarrivedParties());
    }

    static class OnAdvanceTask extends Thread{
        private final Phaser phaser;
        OnAdvanceTask(String name, Phaser phaser) {
            super(name);
            this.phaser = phaser;
        }

        @Override
        public void run() {
            try {

                sout();
                TimeUnit.SECONDS.sleep(1);
                if (getName().equals("Alex")){
                    System.out.println(phaser.isTerminated());
                    sout();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        private void sout() {
            System.out.println(getName() + " I am start and the phase " + phaser.getPhase());
            phaser.arriveAndAwaitAdvance();
            System.out.println(getName() + " I am end !");
        }
    }
}

結果

Jack I am start and the phase 0
Alex I am start and the phase 0
Alex I am end !
Jack I am end !
true
Alex I am start and the phase -2147483647
Alex I am end !
2
0
  • 默認情況,當別人調用arriveAndDeregister時,使註冊的數量減到0時,直接不會陷入阻塞,返回true,相當於銷燬掉

監控子線程任務

public int awaitAdvance(int phase)
public int awaitAdvanceInterruptibly(int phase) throws InterruptedException
  • 相當於起到監控的作用
  • 如果子線程還沒有執行完成,主線程就會阻塞
  • 相較而言,可以不用增加註冊量

舉例

public static void main(String[] args) throws InterruptedException {
    final Phaser phaser = new Phaser(4);

    for (int i = 0; i < 4; i++) {
        new AwaitAdvance(i,phaser).start();
    }
    //等待全部任務進行完成
    phaser.awaitAdvance(phaser.getPhase());
    System.out.println("The phase 1 work finish done.");
}

強制關閉

public void forceTermination()
public boolean isTerminated()
  • 強制關閉phaser,但是如果線程陷入阻塞,不會喚醒

調試API

獲取階段數

public final int getPhase()
  • 返回當前相位數。 最大相位數爲Integer.MAX_VALUE
  • 每增加一輪就會加一

舉例

public class PhaserTest {
    public static void main(String[] args) {
        final Phaser phaser = new Phaser(1);
        System.out.println(phaser.getPhase());

        phaser.arriveAndAwaitAdvance();
        System.out.println(phaser.getPhase());

        phaser.arriveAndAwaitAdvance();
        System.out.println(phaser.getPhase());

        phaser.arriveAndAwaitAdvance();
        System.out.println(phaser.getPhase());

    }
}

結果

0
1
2
3

獲取註冊的數

public int getRegisteredParties()
  • 獲得註冊的線程數,相當於Countdown初始的的計數器
  • 可以動態更改

獲得到達和未到達的數目

public int getArrivedParties()
public int getUnarrivedParties()

getArrivedParties

  • 獲得已經到達的線程數,和沒有到達的線程數

getUnarrivedParties

  • 獲得沒有到達的線程數,和沒有到達的線程數

總結

  • Phaser 可以通過register() 方法和arriveAndDeregister() 方法,動態的增加或者減少註冊量
  • 使用arriveAndAwaitAdvance,相當於CyclicBarrier機制
  • 使用arrive,相當於countdown機制
  • 可以利用awaitAdvance,讓主線程等待子線程全部完成任務
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章