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());
}
}
运行结果演示: