java并发编程-Phaser的使用

Phaser介绍

在生活中我们会遇到这样的场景:我们把一个任务分成多个阶段来做,每个阶段可以由多人来做,当一个阶段所有人的任务都完成后,才能进入下一个阶段,按这样的方式来完成任务直到任务完成。

类似于一个阶段性事务。那么在java编程中如何实现呢?可以通过并发包中的Phaser来模拟实现该场景。

Java中的Phaser也是一个同步barrier(屏障),就像CountDownLatch和CyclicBarrier一样,线程需要在barrier处等待,直到所有线程都达到barrier,也就是barrier被触发。Phaser通过在多个阶段同步线程提供了更大的灵活性。在每个阶段,线程可以动态注册和注销。

  • 使用Phaser,可以在多个phases上重复使用相同的phases实例进行同步。
  • 一旦所有线程注册一个阶段到达barrier,该阶段即被视为完成,下一个阶段开始。
  • 也可以使用Phaser同步单个phase,但是在用于同步多个phase时,它会更加有用。

使用Phaser的步骤:

  • 首先创建一个Phaser实例。
  • 通过调用register()方法向相位器注册parties,您也可以使用构造函数,其中将parties数目作为参数传递给它。
  • 通过调用arrive() 方法来通知party已经到达该phase,一旦所有注册对象都到达某个phase,则认为该phase已完成。
  • 每个phaser对象都有一个与之关联的phase号,phase号从0开始,并在所有各个注册对象到达时加1。

Phaser的基本用法

以下代码展示了Phaser的基本使用。

把整个任务分成了两个阶段,分别由两个任务执行对象(FirstTask和SecondTask)来完成,第1个阶段启动了3个FirstTask的线程,第2个阶段启动了2个SecondTask的线程。

在运行时,第1阶段的3个线程执行完成后,才能进入第2阶段,第2个阶段的所有线程结束时,任务才算完成。

package PhaserExample;

import java.util.concurrent.Phaser;

public class PhaserDemo2 {
    public static void main(String[] args) {
        Phaser ph = new Phaser(1);

        // starting 3 threads
        for(int i = 0; i < 3; i++)
            new Thread(new FirstTask("Thread-"+i, ph)).start();

        int curPhase = ph.getPhase();
        System.out.println(ph.getPhase() + " phase start..., all " + ph.getRegisteredParties() + " parties.\n");

        // This is to make main thread wait
        ph.arriveAndAwaitAdvance(); // 第0阶段结束,第1阶段开始

        for(int i = 0; i < 2; i++)
            new Thread(new SecondTask("Thread-"+i, ph)).start();

        System.out.println(ph.getPhase() + " phase start ..., all " + ph.getRegisteredParties() + " parties.\n");

        ph.arriveAndAwaitAdvance();     // 第1阶段结束,开始第2阶段

        // 第1阶段结束,开始第xx阶段
        System.out.println("\nPhase "+ ph.getPhase() +" in Main-2 start ..., " + ph.getRegisteredParties() + " parties.");

        // deregistering the main thread
        ph.arriveAndDeregister();

    }
}

class FirstTask implements Runnable {
    private String threadName;
    private Phaser ph;

    FirstTask(String threadName, Phaser ph){
        this.threadName = threadName;
        this.ph = ph;
        ph.register();
    }
    @Override
    public void run() {
        int curphase = ph.getPhase();
        System.out.println(threadName + " phase: " + curphase + " start ...");
        // parties will wait here
        ph.arriveAndAwaitAdvance();

        System.out.println("Thread: " + threadName + " in FirstTask.. Phase: " + curphase + " completed");
        ph.arriveAndDeregister();

    }
}

class SecondTask implements Runnable {
    private String threadName;
    private Phaser ph;

    SecondTask(String threadName, Phaser ph){
        this.threadName = threadName;
        this.ph = ph;
        ph.register();
    }

    @Override
    public void run() {
        int curphase = ph.getPhase();
        System.out.println(threadName + ",phase: " + curphase + " start ...");

        ph.arriveAndAwaitAdvance();

        System.out.println("Thread: " + threadName + " in SecondTask.. Phase: " + curphase + " completed");

        ph.arriveAndDeregister();
    }
}

数据处理pipeline

以下模拟任务的分阶段,每个阶段多线程执行的流程。把一个数组,分成了两个阶段来进行处理,每个阶段可以有多个worker线程。当第一个阶段的所有worker完成工作后,再开始第二个阶段的工作,直到任务完成。

第一个阶段的工作是把数组中的值都加1。第二个阶段的工作是把数组中的值都加上100。最后输出最终结果。

package PhaserExample;

import java.util.concurrent.Phaser;

public class ArrayMultipleProcesss {

    public static void main(String[] args) {
        int[] data1 = {1,2,3,4,5,6,7,8,9,10,11,12};
        int TotalTaskNum = data1.length;

        int Task1WorkerNumber = 3, Task2WorkerNumber=2;

        Phaser ph = new Phaser(1);

        // starting 3 threads
        for(int i = 0; i < Task1WorkerNumber; i++) {
            SubTask sub = SplitTask(TotalTaskNum, i, Task1WorkerNumber);
            new Thread(new Process1("Thread-" + i, ph, data1, sub.start,
                                    sub.end)).start();
        }

        System.out.println(ph.getPhase() + " phase start..., all " + 
                           ph.getRegisteredParties() + " parties.\n");

        // This is to make main thread wait
        ph.arriveAndAwaitAdvance(); // 第0阶段结束,第1阶段开始
        PrintArray(data1);

        for(int i = 0; i < Task2WorkerNumber; i++) {
            SubTask sub = SplitTask(TotalTaskNum, i, Task2WorkerNumber);
            new Thread(new Process2("Thread-" + i, ph, data1, sub.start,
                                    		sub.end)).start();
        }

        System.out.println(ph.getPhase() + " phase start ..., all " + 
                           ph.getRegisteredParties() + " parties.\n");

        ph.arriveAndAwaitAdvance();     // 第1阶段结束,开始第2阶段
        PrintArray(data1);

        // 第1阶段结束,开始第xx阶段
        System.out.println("\nPhase "+ ph.getPhase() +" in Main-2 start ..., " + 
                           ph.getRegisteredParties() + " parties.");

        // deregistering the main thread
        ph.arriveAndDeregister();

        PrintArray(data1);
    }

  	// 把任务进行拆分,分到每一个线程需要处理的区间
    public static SubTask SplitTask(int total, int curindex, int nworker) {
        int start= 0;
        int end = 0;
				// System.out.println(total + "," + curindex + "," + nworker);
        int gap = total / nworker;
        start = curindex * gap;
        if (curindex == nworker-1)
            end = total - 1;
        else
            end = start + gap - 1;
				// System.out.println(start + "->" + end);
        return new SubTask(start, end);
    }

    public static void PrintArray(int[] a) {
        synchronized (a) {
            for (int i = 0; i < a.length; i++)
                System.out.print(a[i] + ",");

            System.out.println();
        }
    }

}


class SubTask {
    public int start;
    public int end;

    public SubTask(int start, int end) {
        this.start = start;
        this.end = end;
    }
}



/**
 *  第一次处理实现每个数据+1的操作。
 * */
class Process1 implements Runnable {
    private String threadName;
    private Phaser ph;
    private int[] data;
    private int s, e;

    Process1(String threadName, Phaser ph, int data[], int start, int end){
        this.threadName = threadName;
        this.ph = ph;
        this.data = data;
        this.s = start;
        this.e = end;

        ph.register();
    }
    @Override
    public void run() {
        int curphase = ph.getPhase();
        System.out.println(threadName + " phase: " + curphase + " start ...");
        System.out.println(s + "--" + e);
      	// 处理任务
        synchronized (data) {
            for (int i = s; i <= e; i++) {
                data[i] += 1;
            }
        }
        ph.arriveAndAwaitAdvance();

        System.out.println("Thread: " + threadName + " in FirstTask.. Phase: " +
                           curphase + " completed");
        ph.arriveAndDeregister();

    }
}


/**
 * 第2阶段实现:数组中的每个数+100的操作
 */
class Process2 implements Runnable {
    private String threadName;
    private Phaser ph;
    private int data2[];
    private int s, e;

    Process2(String threadName, Phaser ph, int data[], int start, int end){
        this.threadName = threadName;
        this.ph = ph;
        this.data2 = data;
        this.s = start;
        this.e = end;

        ph.register();
    }

    @Override
    public void run() {
        int curphase = ph.getPhase();
        System.out.println(threadName + ",phase: " + curphase + " start ...");
      	// 处理任务
        synchronized (data2) {
            for (int i = s; i <= e; i++)
                data2[i] += 100;
        }
        ph.arriveAndAwaitAdvance();

        System.out.println("Thread: " + threadName + " in SecondTask.. Phase: " +
                           curphase + " completed");
        ph.arriveAndDeregister();
    }
}

小结

本文介绍了如何使用Phaser,并通过两个例子说明了在实际中的使用。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页