FutureTask 有点类似Runnable,都可以通过Thread来启动,不过FutureTask可以返回执行完毕的数据,并且FutureTask的get方法支持阻塞。

由于:FutureTask可以返回执行完毕的数据,并且FutureTask的get方法支持阻塞这两个特性,我们可以用来预先加载一些可能用到资源,然后要用的时候,调用get方法获取(如果资源加载完,直接返回;否则继续等待其加载完成)。

下面通过两个例子来介绍下:

1、使用FutureTask来预加载稍后要用的的数据。


[java]  view plain  copy

1. package com.zhy.concurrency.futuretask;  
2.   
3. import java.util.concurrent.Callable;  
4. import java.util.concurrent.ExecutionException;  
5. import java.util.concurrent.FutureTask;  
6.   
7. /**
8.  * 使用FutureTask来提前加载稍后要用到的数据
9.  * 
10.  * @author zhy
11.  * 
12.  */  
13. public class PreLoaderUseFutureTask  
14. {  
15. /**
16.      * 创建一个FutureTask用来加载资源
17.      */  
18. private final FutureTask<String> futureTask = new FutureTask<String>(  
19. new Callable<String>()  
20.             {  
21. @Override  
22. public String call() throws Exception  
23.                 {  
24. 3000);  
25. return "加载资源需要3秒";  
26.                 }  
27.             });  
28.   
29. public final Thread thread = new Thread(futureTask);  
30.   
31. public void start()  
32.     {  
33.         thread.start();  
34.     }  
35.   
36. /**
37.      * 获取资源
38.      * 
39.      * @return
40.      * @throws ExecutionException 
41.      * @throws InterruptedException 
42.      */  
43. public String getRes() throws InterruptedException, ExecutionException  
44.     {  
45. return futureTask.get();//加载完毕直接返回,否则等待加载完毕  
46.   
47.     }  
48.   
49. public static void main(String[] args) throws InterruptedException, ExecutionException  
50.     {  
51.   
52. new PreLoaderUseFutureTask();  
53. /**
54.          * 开启预加载资源
55.          */  
56.         task.start();  
57. // 用户在真正需要加载资源前进行了其他操作了2秒  
58. 2000);  
59.   
60. /**
61.          * 获取资源
62.          */  
63. ":开始加载资源");  
64.         String res = task.getRes();  
65.         System.out.println(res);  
66. ":加载资源结束");  
67.     }  
68.   
69. }



运行结果:


[java]  view plain  copy

1. 1400902789275:开始加载资源  
2. 加载资源需要3秒  
3. 1400902790275:加载资源结束


可以看到,本来加载资源的时间需要3秒,现在只花费了1秒,如果用户其他操作时间更长,则可直接返回,极大增加了用户体验。

2、看下Future的API


java多线程并发包类 java 多线程包_加载

可以看到Future的API,还是比简单的,见名知意的感觉,get( long , TimeUnit )还能支持,设置最大等待时间,比如某个操作耗时太长,就可以取消了。

3、FutureTask模拟,用户在线观看电子书的预加载功能

用户观看当前页时,后台预先把下一页加载好,这样可以大幅度提高用户的体验,不需要每一页都等待加载,用户会觉得此电子书软件很流畅,哈哈,用户觉得好,才是真的好。


[java]  view plain  copy

1. package com.zhy.concurrency.futuretask;  
2.   
3. import java.util.concurrent.Callable;  
4. import java.util.concurrent.ExecutionException;  
5. import java.util.concurrent.FutureTask;  
6.   
7.   
8. /**
9.  * 使用FutureTask模拟预加载下一页图书的内容
10.  * 
11.  * @author zhy
12.  * 
13.  */  
14. public class BookInstance  
15. {  
16.   
17. /**
18.      * 当前的页码
19.      */  
20. private volatile int currentPage = 1;  
21.   
22. /**
23.      * 异步的任务获取当前页的内容
24.      */  
25. new FutureTask<String>(  
26. new Callable<String>()  
27.             {  
28. @Override  
29. public String call() throws Exception  
30.                 {  
31. return loadDataFromNet();  
32.                 }  
33.             });  
34.   
35. /**
36.      * 实例化一本书,并传入当前读到的页码
37.      * 
38.      * @param currentPage
39.      */  
40. public BookInstance(int currentPage)  
41.     {  
42. this.currentPage = currentPage;  
43. /**
44.          * 直接启动线程获取当前页码内容
45.          */  
46. new Thread(futureTask);  
47.         thread.start();  
48.     }  
49.   
50. /**
51.      * 获取当前页的内容
52.      * 
53.      * @return
54.      * @throws InterruptedException
55.      * @throws ExecutionException
56.      */  
57. public String getCurrentPageContent() throws InterruptedException,  
58.             ExecutionException  
59.     {  
60.         String con = futureTask.get();  
61. this.currentPage = currentPage + 1;  
62. new Thread(futureTask = new FutureTask<String>(  
63. new Callable<String>()  
64.                 {  
65. @Override  
66. public String call() throws Exception  
67.                     {  
68. return loadDataFromNet();  
69.                     }  
70.                 }));  
71.         thread.start();  
72. return con;  
73.     }  
74.   
75. /**
76.      * 根据页码从网络抓取数据
77.      * 
78.      * @return
79.      * @throws InterruptedException
80.      */  
81. private String loadDataFromNet() throws InterruptedException  
82.     {  
83. 1000);  
84. return "Page " + this.currentPage + " : the content ....";  
85.   
86.     }  
87.   
88. public static void main(String[] args) throws InterruptedException,  
89.             ExecutionException  
90.     {  
91. new BookInstance(1);  
92. for (int i = 0; i < 10; i++)  
93.         {  
94. long start = System.currentTimeMillis();  
95.             String content = instance.getCurrentPageContent();  
96. "[1秒阅读时间]read:" + content);  
97. 1000);  
98.             System.out.println(System.currentTimeMillis() - start);  
99.         }  
100.   
101.     }  
102. }


输出结果:


[java]  view plain  copy

1. [1秒阅读时间]read:Page 1 : the content ....  
2. 2001  
3. [1秒阅读时间]read:Page 2 : the content ....  
4. 1000  
5. [1秒阅读时间]read:Page 3 : the content ....  
6. 1001  
7. [1秒阅读时间]read:Page 4 : the content ....  
8. 1000  
9. [1秒阅读时间]read:Page 5 : the content ....  
10. 1001



可以看到,除了第一次观看当前页需要等待网络加载数据的过程(输出的:2001,1000是加载耗时,1000是用户阅读时间),接下来的页面都是瞬间返回(输出的1000是用户阅读时间),完全不需要等待。

代码都是为了讲解FutureTask的应用场景,,,请勿直接在项目中使用。