線程技術可以讓我們的程序同時做多件事情,線程的工作模式有很多,常見的一種模式就是處理網站的並發,今天我來說說線程另一種很常見的模式,這個模式和前端里的ajax類似:瀏覽器一個主線程執行javascript,頁面渲染等操作,當我們使用ajax向服務端發起請求,由於這個過程很慢,ajax的異步模式可以讓我們無需一直等待服務端的響應,而在這個等待結果時間里做其他的事情,這個模式在線程技術力稱之為Future模式。
Future模式和我前面文章里說到的html5技術里的worker技術差不多,當我們一個程序執行流里某個操作特別耗時,我們可以另起一個線程專門執行這個繁瑣耗時的任務,主線程則可以做其他的事情,下面是我自己找到的一個實現原生Future模式的代碼,它主要參入者如下:
TestMain.java:測試程序入口,主要是調用Client類,向Client發送請求;
Client.java:返回Data對象,立即返回FutureData,並開啟ClientThread線程裝配RealData;
Data.java:返回數據接口;
FutureData.java:Future數據,構造快,但是是一個虛擬的數據,需要裝配RealData;
RealData.java:真實數據,其構造是比較慢的。
詳細代碼如下:
Data接口:
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毫秒
好了,本文寫完了。
