java并发编程实战手册第三章同步辅助类Phaser

java 1.7新增的特性
本案例简介:

5.并发阶段任务的运行(http://blog.csdn.net/escaflone/article/details/10418651
Phaser允许执行并发多阶段的任务。当我们有并发任务并且需要分解成几步执行的时候,这种机制非常好。
Phaser类机制是在每一步结束的位置对线程进行同步,当所有的线程都完成了这一步,才允许执行下一步。
跟其他同步工具一样,必须对Phaser类中参与同步操作的任务数量进行初始化,不同的是,我们可以动态地增加或减少任务数量。

使用介绍:
4.Phaser : 把并发任务分成多个阶段运行,在开始下一个阶段之前,当前阶段的所有线程都必须执行完成。java 7中的新特性。
适用场景:
1.允许并发多阶段任务,当我们有并发任务并且需要分解成几步执行时,这种机制就非常有用。该机制是在每一步结束的位置对线程进行同步,当所有的线程都完成了这一步,才允许执行
下一步。
说明:在F/J框架中的子任务之间要进行同步,应该优先考虑Phaser。
Phaser把多个线程写作执行的任务划分成多个阶段,编程时演明确各个阶段的任务,每个阶段都可以有任意个参与者,
线程可以随时注册并参与到某个阶段,当一个阶段中所有线程都成功完成后,Phaser的onAdvance()被调用,
可以通过覆盖添加自定义处理逻辑(类似CyclicBarrier使用的Ruunable接口),然后Phaser类会自动进入下一个阶段,如此循环直到Phaser不再包含任何参与者
Phaser创建后,初试阶段编号为0,构造函数中指定初始参与个数。
主要方法
1.register(),bulkRegister():动态添加一个或者多个参与者,这些参与者将被当成没有执行完本阶段的线程。
2.arrive():某个参与完成任务后调用,该方法不会等待其他参与者都完成当前状态,所以不会与其他线程同步,应该小心使用。
3.arriveAndDeregister():任务完成,取消自己的注册。
4.arriveAndAwaitAdvance():自己完成等待其他参与者完成,进入阻塞状态,直到Phaser成功进入下一个阶段。
5.awaitAdvance(int phase):如果传入的阶段参数与当前节点一致,这个方法会将当前线程置入休眠,直到这个阶段的所有参与者都运行完成,如果传入的阶段参数与当前阶段
不一致,这个方法将立即返回。
6.awaitAdvanceInterruptibly(int phaser):这个方法与5是一样的,不同之处在于如果在这个方法中休眠的线程被中断,将抛出InterruptedException
7.forceTermination():该方法强制将phaser进入终止状态,这个方法不管phaser中是否存在注册的参与线程,当一个参与的线程出现错误的时候,强制终止phaser是很有意义的。
当一个phaser处于终止状态的时候,awaitAdvance()和arriveAndAwaitAdvance()方法会立即返回一个负数,而不是一个正值。
8.onAdvance():在Phaser阶段改变的时候会被自动执行,一般我们会重写这个方法用来在每个阶段改变的时候做一些事情。 该方法返回布尔值用来表明phaser是否终止,
false表示没有终止,因而线程可以继续执行其他的阶段,如果返回值为true,则phaser仍然唤醒等待的线程,但是状态已经变成终止状态了,所以继续调用phaser的方法将立即返回,
并且isTerminated()方法也将返回true

说明:awaitAdvance(),awaitAdvanceInterruptibly(),等待phaser进入下一个阶段,参数为当前阶段的编号,后者可以设置超时和处理中断请求。
另外,Phaser的一个重要特征是多个Phaser可以组成树形结构,Phaser提供了构造方法来指定当前对象的父对象,当一个子对象参与者>0,会自动注册到父
对象中,当子对象的参与者=0,自动解除注册。
一个Phaser对象有两种状态
 1.活跃状态(Active)当存在参与同步的线程的时候,Phaser就是活跃的,并且每一个阶段结束的时候进行同步。java API中并没有提到该状态
 2.终止状态(Termination):当所有参与同步的线程都取消注册的时候,Phaser就处于终止状态了,这种状态下Phaser没有任何参与者。

一、并发阶段任务的运行
本实例同步三个并发任务,三个任务在三个不同的文件夹和子文件夹中查找过去24小时内修改过的扩展名为.log的文件。分三个步骤执行
1.获得.log文件
2.对1进行过滤,获得修改时间在24小时内的文件
3.将结果打印到控制台。

1.FileSearch: 文件查找类

public class FileSearch implements Runnable{

    private String initPath;  //存储查找的文件夹

    private String end;   //存储查找文件的扩展名

    private Phaser phaser;  //用来控制任务不同阶段的同步

    private List<String> results;

    public FileSearch(String initPath, String end, Phaser phaser) {
        this.initPath = initPath;
        this.end = end;
        this.phaser = phaser;
        results = new ArrayList<String>();
    }

    /**
     * 
     * 作者:fcs
     * 描述:处理文件夹的所有文件夹和子文件夹,对于每个文件夹这个方法将递归调用,对于每个文件,这个方法
     * 将调用fileProcess()方法
     * 说明:
     * 返回:
     * 参数:
     * 时间:2015-4-23
     */
    private void directoryProcess(File file){
        File list [] =  file.listFiles();

        if(list != null){
            for(int i = 0;i < list.length;i++){
                if(list[i].isDirectory()){
                    directoryProcess(list[i]);
                }else{

                }
            }
        }
    }

    /**
     * 
     * 作者:fcs
     * 描述:查找传入文件的对象的扩展名是不是我们指定的。
     * 说明:
     * 返回:
     * 参数:
     * 时间:2015-4-23
     */
    private void fileProcess(File file){
        if(file.getName().endsWith(end)){
            results.add(file.getAbsolutePath());
        }
    }
    /**
     * 
     * 作者:fcs
     * 描述:对第一个阶段查找到的文件列表进行过滤,将不是过去24小时修改过的文件删除,
     * 
     * 说明:
     * 返回:
     * 参数:
     * 时间:2015-4-23
     */
    private void filterResult(){
        List<String> newResult = new ArrayList<String>();
        long actualDate = new Date().getTime();

        for(int i = 0;i < results.size();i++){
            File file = new File(results.get(i));
            long fileDate = file.lastModified();

            if(actualDate - fileDate <TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS)){
                newResult.add(results.get(i));
            }

        }
        results = newResult;
    }

    /**
     * 
     * 作者:fcs
     * 描述:将在第一阶段和第二阶段结束的时候被调用
     * 用来检查结果集是不是空的
     * 说明:如果是空的则调用phaser.arriveAndDeregister()方法
     * 
     * 返回:
     * 参数:
     * 时间:2015-4-23
     */
    public boolean checkResult(){
        if(results.isEmpty()){
            System.out.printf("%s: phase %d: 0 results.\n",Thread.currentThread().getName(),phaser.getPhase());
            System.out.printf("%s: Phase %d End.\n",Thread.currentThread().getName(),phaser.getPhase());
            phaser.arriveAndDeregister();
            return false;
        }else{
            System.out.printf("%s:Phase %d: %d results.\n",Thread.currentThread().getName(),phaser.getPhase(),results.size());
            /*通知phaser对象当前线程已经完成当前阶段,需要被阻塞直到其他线程也都完成当前阶段*/
            phaser.arriveAndAwaitAdvance();
            return true;
        }
    }

    /**
     * 
     * 作者:fcs
     * 描述:
     * 说明:将结果集元素打印到控制台
     * 返回:
     * 参数:
     * 时间:2015-4-23
     */
    private void showInfo(){
        for(int i =0 ; i < results.size();i++){
            File file = new File(results.get(i));
            System.out.printf("%s: %s\n.",Thread.currentThread().getName(),file.getAbsolutePath());
        }
    }

    @Override
    public void run() {
        phaser.arriveAndAwaitAdvance();  //调用该方法等待所有线程都被创建后再开始
        System.out.printf("%s: Starting .\n",Thread.currentThread().getName());

        File file = new File(initPath);
        if(file.isDirectory()){
            directoryProcess(file);
        }
        //并发任务的第一阶段
        //使用该方法检查结果集是不是空的,如果是结束对应线程,并且用关键字return
        if(!checkResult()){
            return;
        }

        //并发任务的第二阶段
        //使用该方法过滤结果集
        filterResult();

        if(!checkResult()){
            return;
        }
        //并发任务的第三阶段
        //使用该方法将最终的结果集打印到控制台,并且撤销线程的注册,然后将线程完成的信息打印到控制台
        showInfo();
        phaser.arriveAndDeregister();
        System.out.printf("%s: work complete .\n",Thread.currentThread().getName());
    }   
}

2.测试类

public class Main {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(3);
        FileSearch system = new FileSearch("c:\\windows", "log", phaser);

        FileSearch apps = new FileSearch("c:\\Program Files", "log", phaser);

        FileSearch documents = new  FileSearch("c:\\Documents And Settings","log",phaser);

        Thread systemThread = new Thread(system,"system");
        systemThread.start();

        Thread appsThread = new Thread(apps,"apps");
        appsThread.start();

        Thread documentsThread = new Thread(apps,"documents");

        documentsThread.start();

        try {
            systemThread.join();
            appsThread.join();
            documentsThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这里如果运行的话,有的电脑可能没有在24小时内修改过的.log文件。这里我模拟了一下;
文件搜索模拟

二、并发阶段中的任务切换
1.自定义Phaser

package cn.fans.chapter3.six;

import java.util.concurrent.Phaser;
/**
 * 
 * @author fcs
 * @date 2015-5-1
 * 描述:并发阶段任务中的阶段切换
 * 说明:继承Phaser对象,并重写onAdvance对象
 */
public class MyPhaser extends Phaser{
    /**
     * 在每个阶段任务完成后,进入下一个阶段时会执行该方法
     * 可以处理阶段性要处理的事情
     */
    @Override
    protected boolean onAdvance(int phase, int registeredParties) {
        switch(phase){
            case 0:
                return studentsArrived();
            case 1:
                return finishFirstExersize();
            case 2:
                return finishSecondExersize();
            case 3:
                return finishExam();
                default :
                    return true;
        }
    }

    private boolean studentsArrived(){
        System.out.printf("Phaser: The exam are going to start. The Students are ready");
        System.out.printf("Phaser: We have %d students.\n",getRegisteredParties());
        return false;
    }

    private boolean finishFirstExersize(){
        System.out.println("Phaser: All the students have finished the first exersie.\n");
        System.out.println("Phaser: It's time for the second one\n");
        return false;
    }

    private boolean finishSecondExersize(){
        System.out.println("Phaser: All the students have finished the second exersie.\n");
        System.out.println("Phaser: It's time for the second one\n");
        return false;
    }

    private boolean finishExam(){
        System.out.println("Phaser: All the students have finished the exam.\n");
        System.out.println("Phaser: Thank you for your time\n");
        return true;
    }
}

2.学生类

package cn.fans.chapter3.six;

import java.util.Date;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;

public class Student implements  Runnable{

    private Phaser phaser;

    public Student(Phaser phaser) {
        this.phaser = phaser;
    }

    @Override
    public void run() {
        System.out.printf("%s: has arrived to do the exam.%s\n",Thread.currentThread().getName(),new Date());
        phaser.arriveAndAwaitAdvance();

        System.out.printf("%s: is going to do the first exercise %s.\n",Thread.currentThread().getName(),new Date());
        doExceercise1();
        System.out.printf("%s: has down the first exercise %s.\n",Thread.currentThread().getName(),new Date());
        phaser.arriveAndAwaitAdvance();


        System.out.printf("%s: is going to do the second exercise %s.\n",Thread.currentThread().getName(),new Date());
        doExceercise2();
        System.out.printf("%s: has down the second exercise %s.\n",Thread.currentThread().getName(),new Date());
        phaser.arriveAndAwaitAdvance();

        System.out.printf("%s: is going to do the third exercise %s.\n",Thread.currentThread().getName(),new Date());
        doExceercise3();
        System.out.printf("%s: has down the second exercise %s.\n",Thread.currentThread().getName(),new Date());

        phaser.arriveAndAwaitAdvance();

    }

    private void doExceercise1(){
        long duration = (long)(Math.random()*10);
        try {
            TimeUnit.SECONDS.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void doExceercise2(){
        long duration = (long)(Math.random()*10);
        try {
            TimeUnit.SECONDS.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void doExceercise3(){
        long duration = (long)(Math.random()*10);
        try {
            TimeUnit.SECONDS.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3.测试类

public class Main {
    public static void main(String[] args) {
        MyPhaser myPhaser = new MyPhaser();
        Student [] student = new Student[5];
        for(int i =0 ;i< student.length;i++){
            student[i] = new Student(myPhaser);
            //该方法并没有建立学生对象或者它对应的执行线程与phaser之间的关联。
            //实际上phaser中的参与者数目只是一个数字,phaser与参与者不存在任何关联。
            myPhaser.register();   //通过该方法将student线程对象注册到phaser对象中
        }

        Thread threads [] = new Thread[student.length];
        //创建5个线程
        for(int i =0 ;i < student.length;i++){
            threads[i] = new Thread(student[i],"student"+i);
            threads[i].start();
        }

        //等待线程终止
        for(int i =0 ;i< student.length;i++){
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.printf("Main : The phaser has finished: %s \n",myPhaser.isTerminated());
    }
}

运行结果演示:
阶段性任务完成演示

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值