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

来源:互联网 发布:wol网络唤醒 编辑:程序博客网 时间:2024/06/06 05:10

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());    }}

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

0 0
原创粉丝点击