線程技術可讓咱們的程序同時作多件事情,線程的工做模式有不少,常見的一種模式就是處理網站的併發,今天我來講說線程另外一種很常見的模式,這個模式和前端裏的ajax相似:瀏覽器一個主線程執行javascript,頁面渲染等操做,當咱們使用ajax向服務端發起請求,因爲這個過程很慢,ajax的異步模式可讓咱們無需一直等待服務端的響應,而在這個等待結果時間裏作其餘的事情,這個模式在線程技術力稱之爲Future模式。javascript
Future模式和我前面文章裏說到的html5技術裏的worker技術差很少,當咱們一個程序執行流裏某個操做特別耗時,咱們能夠另起一個線程專門執行這個繁瑣耗時的任務,主線程則能夠作其餘的事情,下面是我本身找到的一個實現原生Future模式的代碼,它主要參入者以下:html
TestMain.java:測試程序入口,主要是調用Client類,向Client發送請求;前端
Client.java:返回Data對象,當即返回FutureData,並開啓ClientThread線程裝配RealData;html5
Data.java:返回數據接口;java
FutureData.java:Future數據,構造快,可是是一個虛擬的數據,須要裝配RealData;ajax
RealData.java:真實數據,其構造是比較慢的。瀏覽器
詳細代碼以下:併發
Data接口:app
package cn.com.xSharp.futurePattern.simple; /** * 數據接口 * @author 俊 * */ public interface Data { public String getData(); }
RealData代碼:異步
package cn.com.xSharp.futurePattern.simple; /** * RealData是最終使用的數據,它構造很慢,所以用sleep來模擬 * @author 俊 * @since 2016-06-21 21:37 */ public class RealData implements Data { protected final String result; public RealData(String param) { StringBuffer sb = new StringBuffer(); for (int i = 0;i < 10;i++){ sb.append(param); try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } } result = sb.toString(); } @Override public String getData() { return result; } }
FutureData代碼:
package cn.com.xSharp.futurePattern.simple; public class FutureData implements Data { protected RealData realData = null;// FutureData對RealData的包裝 protected boolean isReady = false; public synchronized void setRealData(RealData realData){ if (isReady){ return; } this.realData = realData; isReady = true; notifyAll(); } @Override public synchronized String getData() { while (!isReady){ try { wait(); } catch (Exception e) { e.printStackTrace(); } } return realData.result; } }
Client代碼:
package cn.com.xSharp.futurePattern.simple; public class Client { public Data request(final String qryStr){ final FutureData futureData = new FutureData(); new Thread(){ public void run(){ RealData realData = new RealData(qryStr); futureData.setRealData(realData); } }.start(); return futureData; } }
TestMain代碼:
package cn.com.xSharp.futurePattern.simple; public class TestMain { public static void main(String[] args) { Client client = new Client(); Data data = client.request("xtq"); System.out.println("請求完畢!"); try { for (int i = 0;i < 12;i++){ Thread.sleep(100); System.out.println("能夠作作其餘的事情哦...."); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("數據==:" + data.getData()); } }
執行結果:
請求完畢! 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 能夠作作其餘的事情哦.... 數據==:xtqxtqxtqxtqxtqxtqxtqxtqxtqxtq
JDK裏在1.5以後提供了專門Future模式的實現,這裏我使用FutureTask來實現Future模式。
FutureTask在JDK文檔裏的解釋:
可取消的異步計算。利用開始和取消計算的方法、查詢計算是否完成的方法和獲取計算結果的方法,此類提供了對 Future 的基本實現。僅在計算完成時才能獲取結果;若是計算還沒有完成,則阻塞 get 方法。一旦計算完成,就不能再從新開始或取消計算。 可以使用 FutureTask 包裝 Callable 或 Runnable 對象。由於 FutureTask 實現了 Runnable,因此可將 FutureTask 提交給 Executor 執行。 除了做爲一個獨立的類外,此類還提供了 protected 功能,這在建立自定義任務類時可能頗有用。
下面是它的兩個構造函數:
FutureTask(Callable<V> callable) 建立一個 FutureTask,一旦運行就執行給定的 Callable。 FutureTask(Runnable runnable, V result) 建立一個 FutureTask,一旦運行就執行給定的 Runnable,並安排成功完成時 get 返回給定的結果 。
這裏我首先使用第二個構造函數Runnable實現Future模式,代碼以下:
package cn.com.futuretest; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; public class FutureRunnable implements Runnable{ private Result result; // 操做的數據,模擬一個計算須要很長時間的數據 /* 初始化數據 */ public FutureRunnable(Result result) { this.result = result; } @Override public void run() { try { for (int i = 0;i < 10;i++){ Thread.sleep(100);// 每隔100毫秒操做一次數據,模擬數據被長時間計算的場景 result.setData(result.getData() + ":" + "futureRunnable" + i); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { Result r = new Result("xSharp");// 構造測試數據 FutureRunnable futureCallable = new FutureRunnable(r);// 初始化runnable FutureTask<Result> task = new FutureTask<Result>(futureCallable, r); // 構造固定大小爲一個線程的線程池 ExecutorService executorService = Executors.newFixedThreadPool(1); // 執行線程 executorService.execute(task); System.out.println("執行完畢!"); try { for (int i = 0;i < 15;i++){ Thread.sleep(100); System.out.println("數據還在計算中等待中,你能夠作別的事情" + i); } } catch (InterruptedException e) { e.printStackTrace(); } try { System.out.println("打印結果是:" + task.get().getData()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }finally{ System.exit(0); } } }
執行結果:
執行完畢! 數據還在計算中等待中,你能夠作別的事情0 數據還在計算中等待中,你能夠作別的事情1 數據還在計算中等待中,你能夠作別的事情2 數據還在計算中等待中,你能夠作別的事情3 數據還在計算中等待中,你能夠作別的事情4 數據還在計算中等待中,你能夠作別的事情5 數據還在計算中等待中,你能夠作別的事情6 數據還在計算中等待中,你能夠作別的事情7 數據還在計算中等待中,你能夠作別的事情8 數據還在計算中等待中,你能夠作別的事情9 數據還在計算中等待中,你能夠作別的事情10 數據還在計算中等待中,你能夠作別的事情11 數據還在計算中等待中,你能夠作別的事情12 數據還在計算中等待中,你能夠作別的事情13 數據還在計算中等待中,你能夠作別的事情14 打印結果是:xSharp:futureRunnable0:futureRunnable1:futureRunnable2:futureRunnable3:futureRunnable4:futureRunnable5:futureRunnable6:futureRunnable7:futureRunnable8:futureRunnable9
接下來我使用Callable<V> 接口實現FutureTask,代碼以下:
package cn.com.futuretest; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; public class FutureCallable implements Callable<Result>{ private Result result; // 操做的數據,模擬一個計算須要很長時間的數據 /* 初始化數據 */ public FutureCallable(Result result) { this.result = result; } @Override public Result call() throws Exception { try { for (int i = 0;i < 10;i++){ Thread.sleep(100);// 每隔100毫秒操做一次數據,模擬數據被長時間計算的場景 result.setData(result.getData() + ":" + "futureCallable" + i); } } catch (InterruptedException e) { e.printStackTrace(); } return result; } public static void main(String[] args) { long start = System.currentTimeMillis(); Result r = new Result("xSharp");// 構造測試數據 FutureCallable callable = new FutureCallable(r); FutureTask<Result> task = new FutureTask<Result>(callable); // 構造固定大小爲一個線程的線程池 ExecutorService executorService = Executors.newFixedThreadPool(1); // 執行線程 executorService.execute(task); System.out.println("執行完畢!"); long curr01 = System.currentTimeMillis(); System.out.println("任務提交後的耗時:" + (curr01 - start) + "毫秒"); try { for (int i = 0;i < 6;i++){ Thread.sleep(100); System.out.println("數據還在計算中等待中,你能夠作別的事情" + i); } } catch (InterruptedException e) { e.printStackTrace(); } try { System.out.println("打印結果是:" + task.get().getData()); long end = System.currentTimeMillis(); System.out.println("總耗時:" + (end - start) + "毫秒"); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }finally{ System.exit(0); } } }
執行結果以下:
執行完畢! 任務提交後的耗時:6毫秒 數據還在計算中等待中,你能夠作別的事情0 數據還在計算中等待中,你能夠作別的事情1 數據還在計算中等待中,你能夠作別的事情2 數據還在計算中等待中,你能夠作別的事情3 數據還在計算中等待中,你能夠作別的事情4 數據還在計算中等待中,你能夠作別的事情5 打印結果是:xSharp:futureCallable0:futureCallable1:futureCallable2:futureCallable3:futureCallable4:futureCallable5:futureCallable6:futureCallable7:futureCallable8:futureCallable9 總耗時:1010毫秒
這裏我對代碼作了一些調整,一個是加上了執行時間的統計,一個是我將幹其餘事情的程序執行時間變短,小於了線程自己執行的時間,這麼作的目的是想和下面的程序對比,下面的代碼當我執行線程後沒有作其餘的操做,而是直接獲取線程執行的結果,具體代碼以下:
package cn.com.futuretest; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; public class NioFutureCallable implements Callable<Result> { private Result result; // 操做的數據,模擬一個計算須要很長時間的數據 /* 初始化數據 */ public NioFutureCallable(Result result) { this.result = result; } @Override public Result call() throws Exception { try { for (int i = 0;i < 10;i++){ Thread.sleep(100);// 每隔100毫秒操做一次數據,模擬數據被長時間計算的場景 result.setData(result.getData() + ":" + "NioFutureCallable" + i); } } catch (InterruptedException e) { e.printStackTrace(); } return result; } public static void main(String[] args) { long start = System.currentTimeMillis(); Result r = new Result("xSharp");// 構造測試數據 NioFutureCallable callable = new NioFutureCallable(r); FutureTask<Result> task = new FutureTask<Result>(callable); // 構造固定大小爲一個線程的線程池 ExecutorService executorService = Executors.newFixedThreadPool(1); // 執行線程 executorService.execute(task); System.out.println("執行完畢!"); long curr01 = System.currentTimeMillis(); System.out.println("任務提交後的耗時:" + (curr01 - start) + "毫秒"); /* 第一次獲取返回數據 */ try { System.out.println("第一次打印結果是:" + task.get().getData()); long curr02 = System.currentTimeMillis(); System.out.println("第一次獲取結果耗時:" + (curr02 - start) + "毫秒"); } catch (InterruptedException e1) { e1.printStackTrace(); } catch (ExecutionException e1) { e1.printStackTrace(); } try { for (int i = 0;i < 10;i++){ Thread.sleep(100); System.out.println("數據還在計算中等待中,你能夠作別的事情" + i); } } catch (InterruptedException e) { e.printStackTrace(); } try { System.out.println("第二次打印結果是:" + task.get().getData()); long end = System.currentTimeMillis(); System.out.println("總耗時:" + (end - start) + "毫秒"); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }finally{ System.exit(0); } } }
執行結果以下:
執行完畢! 任務提交後的耗時:7毫秒 第一次打印結果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9 第一次獲取結果耗時:1009毫秒 數據還在計算中等待中,你能夠作別的事情0 數據還在計算中等待中,你能夠作別的事情1 數據還在計算中等待中,你能夠作別的事情2 數據還在計算中等待中,你能夠作別的事情3 數據還在計算中等待中,你能夠作別的事情4 數據還在計算中等待中,你能夠作別的事情5 數據還在計算中等待中,你能夠作別的事情6 數據還在計算中等待中,你能夠作別的事情7 數據還在計算中等待中,你能夠作別的事情8 數據還在計算中等待中,你能夠作別的事情9 第二次打印結果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9 總耗時:2012毫秒
咱們看到當咱們直接獲取結果時候,整個主線程都被阻塞了,直到結果返回後纔會執行下面的後續操做,這也就是說若是計算還沒結束,咱們就想獲取結果這樣整個執行流程都將被阻塞,這點在咱們合理使用Future模式時候很重要。
除了使用FutureTask實現Future模式,咱們還可使用ExecutorService的submit方法直接返回Future對象,Future就和我前面設計的原生Future相似,當咱們開始調用時候返回的是一個虛擬結果,其實實際的計算尚未結束,只有等待嗎一下子後結果纔會真正的返回,代碼以下:
package cn.com.futuretest; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class RetFutureCallable implements Callable<Result>{ private Result result; // 操做的數據,模擬一個計算須要很長時間的數據 public RetFutureCallable() { result = new Result("xSharp"); } @Override public Result call() throws Exception { try { for (int i = 0;i < 10;i++){ Thread.sleep(100);// 每隔100毫秒操做一次數據,模擬數據被長時間計算的場景 result.setData(result.getData() + ":" + "RetFutureCallable" + i); } } catch (InterruptedException e) { e.printStackTrace(); } return result; } public static void main(String[] args) { long start = System.currentTimeMillis(); RetFutureCallable callable = new RetFutureCallable(); // 構造固定大小爲一個線程的線程池 ExecutorService executorService = Executors.newFixedThreadPool(1); // 執行線程 Future<Result> r = executorService.submit(callable); System.out.println("執行完畢!"); long curr01 = System.currentTimeMillis(); System.out.println("任務提交後的耗時:" + (curr01 - start) + "毫秒"); try { for (int i = 0;i < 6;i++){ Thread.sleep(100); System.out.println("數據還在計算中等待中,你能夠作別的事情" + i); } } catch (InterruptedException e) { e.printStackTrace(); } try { System.out.println("打印結果是:" + r.get().getData()); long end = System.currentTimeMillis(); System.out.println("總耗時:" + (end - start) + "毫秒"); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }finally{ System.exit(0); } } }
執行結果以下:
執行完畢! 任務提交後的耗時:5毫秒 數據還在計算中等待中,你能夠作別的事情0 數據還在計算中等待中,你能夠作別的事情1 數據還在計算中等待中,你能夠作別的事情2 數據還在計算中等待中,你能夠作別的事情3 數據還在計算中等待中,你能夠作別的事情4 數據還在計算中等待中,你能夠作別的事情5 打印結果是:xSharp:RetFutureCallable0:RetFutureCallable1:RetFutureCallable2:RetFutureCallable3:RetFutureCallable4:RetFutureCallable5:RetFutureCallable6:RetFutureCallable7:RetFutureCallable8:RetFutureCallable9 總耗時:1006毫秒
好了,本文寫完了。