Javaq发基础实践--退ZQ务II
?a href="http://www.aygfsteel.com/jiangshachina/category/53896.html">本系?/a>?a href="http://www.aygfsteel.com/jiangshachina/archive/2013/09/21/404269.html">上一?/a>中所q的退出ƈ发Q务的方式都是ZJDK 5之前的APIQ本文将介绍使用由JDK 5引入的ƈ发工具包中的API来退ZQ务?2013.10.08最后更?
在本pd的前一中讲述了三U退出ƈ发Q务的方式--停止U程Q可取消的Q务;中断Q但都是ZJDK 5之前的API。本将介绍由JDK 5引入的java.concurrent包中的Future来取消Q务的执行?br />
1. Future模式
Future是ƈ发编E中的一U常见设计模式,它相当于是Proxy模式与Thread-Per-Message模式的结合。即Q每ơ都创徏一个单独的U程L行一个耗时的Q务,q且创徏一个Future对象L有实际的d对象Q在来需要的时候再去获取实际Q务的执行l果?br />依然先创Z个用于扫描文件的dFileScannerTaskQ如代码清单1所C,
清单1
public class FileScannerTask implements Runnable {
private File root = null;
private ArrayList<String> filePaths = new ArrayList<String>();
public FileScannerTask(File root) {
if (root == null || !root.exists() || !root.isDirectory()) {
throw new IllegalArgumentException("root must be directory");
}
this.root = root;
}
@Override
public void run() {
travleFiles(root);
}
private void travleFiles(File parent) {
String filePath = parent.getAbsolutePath();
filePaths.add(filePath);
if (parent.isDirectory()) {
File[] children = parent.listFiles();
if (children != null) {
for (File child : children) {
travleFiles(child);
}
}
}
}
public List<String> getFilePaths() {
return (List<String>) filePaths.clone();
}
}
此处的文件扫描Q务,提供了一个getFilePaths()Ҏ以允讔R旉可以取出当前已扫描过的文件的路径(相当于一个Q务快?。然后,创徏一个针对该d的Futurec,如代码清?所C,
清单2
public class FileScannerFuture {
private FileScannerTask task = null;
public FileScannerFuture(FileScannerTask task) {
new Thread(task).start();
this.task = task;
}
public List<String> getResult() {
return task.getFilePaths();
}
}
FileScannerFuture持有FileScannerTask的引用,q创Z个独立的U程来执行该d。在d的执行过E中Q应用程序可以在"未来"的某个时d获取一个Q务的快照Q如代码清单3所C,
清单3
public static void main(String[] args) throws Exception {
FileScannerFuture future = new FileScannerFuture(new FileScannerTask(new File("C:")));
TimeUnit.SECONDS.sleep(1);
List<String> filePaths1 = future.getResult();
System.out.println(filePaths1.size());
TimeUnit.SECONDS.sleep(1);
List<String> filePaths2 = future.getResult();
System.out.println(filePaths2.size());
}
2. 使用q发工具包中的Future实现
前面所展示的Future实现十分的简陋,没有实际应用的意义。用FileScannerFutureQ应用程序在获取filePathsӞ无法得知其获取的是否为最l结果,x法判断FileScannerTask是否已经完成。而且Q也不能在必要时停止FileScannerTask的执行。毫无疑问,由JDK 5引入的ƈ发工具包肯定会提供此cd用工P如FutureTask。ؓ了用ƈ发工具包中的FutureQ需要修改前q的FileScannerTask实现Q让其实现Callable接口Q如代码清单4所C,
清单4
public class FileScannerTask implements Callable<List<String>> {
private File root = null;
private List<String> filePaths = new ArrayList<String>();
public FileScannerTask(File root) {
if (root == null || !root.exists() || !root.isDirectory()) {
throw new IllegalArgumentException("root must be directory");
}
this.root = root;
}
@Override
public List<String> call() {
travleFiles(root);
return filePaths;
}
private void travleFiles(File parent) {
String filePath = parent.getAbsolutePath();
filePaths.add(filePath);
if (parent.isDirectory()) {
File[] children = parent.listFiles();
if (children != null) {
for (File child : children) {
travleFiles(child);
}
}
}
}
public List<String> getFilePaths() {
return (List<String>) filePaths.clone();
}
}
应用E序也要相应的修Ҏ如代码清?所C,使用ExecutorService来提交Q务,q创Z个Future/FutureTask实例?br />清单5
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<List<String>> future = executorService.submit(new FileScannerTask(new File("C:")));
try {
List<String> filePaths = future.get();
System.out.println(filePaths.size());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
此处是调用Future.get()Ҏ来获取Q务的执行l果Q如果Q务没有执行完毕,那么该方法将会被d。该Future实现的好处就是,正常情况下,只有在Q务执行完毕之后才能获取其l果Q以保证该结果是最l执行结果?br />
3. 使用Future取消d
Future除了定义有可获取执行l果的getҎ(get()以及get(long timeout, TimeUnit unit))Q还定义了三个方法:cancel()QisCancelled()以及isDone()Q用于取消Q务,以及判定d是否已被取消、已执行完毕。如代码清单6所C,
清单6
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();

} 其中Qcancel()Ҏ中的boolean参数若ؓtrueQ表C在取消该Q务时Q若执行该Q务的U程仍在q行中,则对其进行中断。如代码清单7所C,若Q务执行超时了Q那么就取消它?br />清单7
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<List<String>> future = executorService.submit(new FileScannerTask(new File("C:")));
try {
List<String> filePaths = future.get(1, TimeUnit.SECONDS);
System.out.println(filePaths.size());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
future.cancel(true);
}
executorService.shutdown();
}
在实际应用中Q取消Q务的原由肯定不仅仅只是超时这么简单,q可能是׃接受C用户的指令。此Ӟ则可能会从另一个独立线E去取消该Q务。除了取消Q务之外,有时q需要取ZQ务中已经生成的部分结果。但Z能够响应d的退出,首先需要修改FileScannerTaskQ得当d被取?中断)Ӟd能够真正的快速停止ƈq回Q如代码清单8所C,
清单8
public class FileScannerTask implements Callable<List<String>> {

private void travleFiles(File parent) {
if (Thread.currentThread().isInterrupted()) {
return;
}
String filePath = parent.getAbsolutePath();
filePaths.add(filePath);
if (parent.isDirectory()) {
File[] children = parent.listFiles();
if (children != null) {
for (File child : children) {
travleFiles(child);
}
}
}
}

} 相应C改应用程序的代码Q如代码清单9所C,
清单9
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
FileScannerTask task = new FileScannerTask(new File("C:"));
final Future<List<String>> future = executorService.submit(task);
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.cancel(true);
}
}).start();
try {
List<String> filePaths = future.get();
System.out.println(filePaths.size());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (CancellationException e) {
List<String> filePaths = task.getFilePaths();
System.out.println("Partly result: " + filePaths.size());
}
executorService.shutdown();
}
׃可知Q此处用Future.cancel(true)的本质依然是利用了线E的中断机制?br />
4. 结
使用Future可以在Q务启动之后的特定时机再去获取d的执行结果。由JDK 5引入的ƈ发工具包中提供的Future实现不仅可以获取d的执行结果,q可以用于取消Q务的执行?/div>
]]>