java如何创建异步任务
1 2 3 4 5 6 7 8 9 10 11 12
| public class TestThread {
public static void main(String[] args) { Runnable task = () -> { System.out.println("异步执行"); }; new Thread(task).start();
System.out.println("同步执行"); } }
|
在java中执行异步任务需要新建一个Thread线程,然后通过Runnable接口实现一个任务,最后将任务通过参数的方式传递给Thread类。
获取异步任务的的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class TestThread {
public static void main(String[] args) { List<String> pdfList = new LinkedList<>(); Runnable task = () -> { String pdf = downloadPdf(); pdfList.add(pdf); }; new Thread(task).start(); System.out.println(pdfList.size()); }
private static String downloadPdf() { return "pdf"; }
}
|
上面这种方式获取异步执行结果是有问题的,输出集合的大小可能为0,这是因为在输出结果的时候异步任务可能还没开始执行。
2.Future解决了什么问题
为了能够在获取到异步任务执行的结果后再继续之后的代码,我们可以使用CountDownLatch或者FutureTask,因为本篇内容是讲Future,所以仅说明Future的使用方法。
为了解决Runnable不能适配没有返回值的问题,jdk使用适配器模式为我们实现了FutureTask类,FutureTask实现了Runnable接口和Future接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class TestThread {
public static void main(String[] args) throws ExecutionException, InterruptedException { List<String> pdfList = new LinkedList<>();
FutureTask<String> task = new FutureTask(() -> { String pdf = downloadPdf(); return pdf; }); new Thread(task).start(); pdfList.add(task.get()); System.out.println(pdfList.size()); }
private static String downloadPdf() { return "pdf"; }
}
|
通过调用FutureTask的get()方法,当前线程会阻塞等待异步线程执行完成并将结果返回。
3.CompetableFuture解决了什么问题
了解了FutureTask之后,我们知道FutureTask.get()方法会阻塞当前线程,等待异步执行结果返回。我们来看下面这个例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class TestThread {
public static void main(String[] args) throws ExecutionException, InterruptedException { List<String> pdfList = new LinkedList<>();
FutureTask<String> task1 = new FutureTask(() -> { String pdf = downloadPdf(); return pdf; }); new Thread(task1).start();
FutureTask<String> task2 = new FutureTask(() -> { String pdf = downloadPdf(); return pdf; }); new Thread(task2).start();
String pdfFont1 = getFontFromPdf(task1.get()); String pdfFont2 = getFontFromPdf(task2.get()); pdfList.add(pdfFont1); pdfList.add(pdfFont2);
System.out.println(pdfList.size()); } private static String getFontFromPdf(String pdf) { return "pdf-font"; }
private static String downloadPdf() { return "pdf"; }
}
|
String pdfFont1 = getFontFromPdf(task1.get());在执行该代码的时候会等待task1执行完成在继续后面的代码,那么如果task2先执行完成的话就要干等着。
这时候我们可以将getFontFromPdf方法也放到task中如下,这样就能避免这个问题。
1 2 3 4 5 6
| FutureTask<String> task1 = new FutureTask(() -> { String pdf = downloadPdf(); return getFontFromPdf(pdf); });
|
另外,我们还能通过CompetableFuture来解决这个问题,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class TestThread {
public static void main(String[] args) throws ExecutionException, InterruptedException { List<String> pdfList = new LinkedList<>();
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> { String pdf = downloadPdf(); return pdf; }).thenApplyAsync(pdf -> getFontFromPdf(pdf));
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> { String pdf = downloadPdf(); return pdf; }).thenApplyAsync(pdf -> getFontFromPdf(pdf));
pdfList.add(task1.get()); pdfList.add(task2.get());
System.out.println(pdfList.size()); }
private static String getFontFromPdf(String pdf) { return "pdf-font"; }
private static String downloadPdf() { return "pdf"; }
}
|
CompletableFuture通过thenApplyAsync方法追加了对方法结果的二次处理,CompletableFuture是jdk8的新特性,设计更符合lamda的流编程。