線程筆記:Future模式

  

  線程技術可讓咱們的程序同時作多件事情,線程的工做模式有不少,常見的一種模式就是處理網站的併發,今天我來講說線程另外一種很常見的模式,這個模式和前端裏的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毫秒

  好了,本文寫完了。

相關文章
相關標籤/搜索