import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
/**
* Java并发API提供的一个非常复杂强大的功能是,能够使用Phaser类运行阶段性的并发任务。当某些并发任务是分成多个步骤来执行时,那么此机制是非常有用的。
* Phaser类提供的机制是在每个步骤的结尾同步线程,所以没有线程开始第二步直到全部线程完成第一步骤。
* 相对于其他同步应用,我们必须初始化Phaser类与这次同步操作有关的任务数,我们可以通过增加或者减少来不断的改变这个数。
* 在这个指南,你将学习如果使用Phaser类来同步3个并发任务。这3个任务会3个不同的文件夹和它们的子文件夹中搜索扩展名是.log并在24小时内修改过的文件。
* 这个任务分为三个步骤:
* 1.在指定的文件夹和子文件夹中获得文件扩展名为.log的文件列表
* 2.过滤第一步的列表中修改超过24小时的文件
* 3.在操控台打印结果
* 在步骤1和步骤2的结尾我们要检查列表是否为空。如果为空,那么线程直接结束运行并从phaser类中淘汰。
* @author liangliangqian
*
*/
//在文件夹递归搜索扩展名为.log并在24小时内修改过的文件
public class FileSearch implements Runnable{
private String initPath;//搜索的文件夹路径
private String end;//搜索文件的扩展名
private List<String> results; //存放找到的符合条件的文件绝对路径
private Phaser phaser; //控制任务的不同phaser的同步
public FileSearch(String initPath,String end,Phaser phaser){
this.initPath = initPath;
this.end = end;
this.phaser = phaser;
results = new ArrayList<String>();
}
@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);
}
if(!checkResults()){
return;
}
filterResults();
if(!checkResults()){
return;
}
showInfo();
phaser.arriveAndDeregister();
System.out.printf("%s: Work completed.\n", Thread.currentThread().getName());
}
//递归搜索文件
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{
fileProcess(list[i]);
}
}
}
}
private void fileProcess(File file){
if(file.getName().endsWith(end)){
results.add(file.getAbsolutePath());
}
}
private void filterResults(){
List<String> newResults = 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.MICROSECONDS.convert(1, TimeUnit.DAYS)){
newResults.add(results.get(i));
}
}
results = newResults;
}
private boolean checkResults(){
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.arriveAndAwaitAdvance();
return true;
}
}
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());
}
phaser.arriveAndAwaitAdvance();
}
}
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(documents,"Documents");
documentsThread.start();
try{
systemThread.join();
appsThread.join();
documentsThread.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Terminated: " + phaser.isTerminated());
}
}
/*
这程序开始创建的Phaser对象是用来在每个phase的末端控制线程的同步。Phaser的构造函数接收参与者的数量作为参数。在这里,Phaser有3个参与者。
这个数向Phaser表示Phaser改变phase之前执行arriveAndAwaitAdvance()方法的线程数,并叫醒正在休眠的线程。
一旦Phaser被创建,我们运行3个线程分别执行3个不同的FileSearch对象。
在例子中,我们使用Windows operating system的路径。如果你使用的是其他操作系统,那么修改成适应你的环境的路径。
FileSearch对象的 run() 方法中的第一个指令是调用Phaser对象的 arriveAndAwaitAdvance() 方法。像之前提到的,Phaser知道我们要同步的线程的数量。当某个线程调用此方法,Phaser减少终结actual phase的线程数,并让这个线程进入休眠 直到全部其余线程结束phase。在run() 方法前面调用此方法,没有任何 FileSearch 线程可以开始他们的工作,直到全部线程被创建。
在phase 1 和 phase 2 的末端,我们检查phase 是否生成有元素的结果list,或者它没有生成结果且list为空。在第一个情况,checkResults() 方法之前提的调用 arriveAndAwaitAdvance()。在第二个情况,如果list为空,那就没有必要让线程继续了,就直接返回吧。但是你必须通知phaser,将会少一个参与者。为了这个,我们使用arriveAndDeregister()。它通知phaser线程结束了actual phase, 但是它将不会继续参见后面的phases,所以请phaser不要再等待它了。
在phase3的结尾实现了 showInfo() 方法, 调用了 phaser 的 arriveAndAwaitAdvance() 方法。这个调用,保证了全部线程在同一时间结束。当此方法结束执行,有一个调用phaser的arriveAndDeregister() 方法。这个调用,我们撤销了对phaser线程的注册,所以当全部线程结束时,phaser 有0个参与者。
最后,main() 方法等待3个线程的完成并调用phaser的 isTerminated() 方法。当phaser 有0个参与者时,它进入termination状态,此状态与此调用将会打印true到操控台。
Phaser 对象可能是在这2中状态:
Active: 当 Phaser 接受新的参与者注册,它进入这个状态,并且在每个phase的末端同步。 在此状态,Phaser像在这个指南里解释的那样工作。此状态不在Java 并发 API中。
Termination: 默认状态,当Phaser里全部的参与者都取消注册,它进入这个状态,所以这时 Phaser 有0个参与者。更具体的说,当onAdvance() 方法返回真值时,Phaser 是在这个状态里。如果你覆盖那个方法,你可以改变它的默认行为。当 Phaser 在这个状态,同步方法 arriveAndAwaitAdvance()会 立刻返回,不会做任何同步。
Phaser 类的一个显著特点是你不需要控制任何与phaser相关的方法的异常。不像其他同步应用,线程们在phaser休眠不会响应任何中断也不会抛出 InterruptedException 异常。只有一个异常会在下面的‘更多’里解释。
它展示了前2个phases的执行。你可以发现Apps线程在phase 2 结束它的运行由于list 为空。当你执行例子,你会发现一些线程比其他的线程更快结束phase,但是他们必须等待其他全部结束然后才能继续。
*/
Phaser
最新推荐文章于 2024-09-11 23:11:24 发布