Future接口Callable接口的使用

在併發處理數據時,一個典型的場景是: 咱們將須要處理的而且很是耗時的任務(例如訪問網絡IO)交給線程池中的一個線程去處理,咱們當前的線程執行其餘的任務,而後當交給線程池的任務執行完成後,咱們再獲取執行的結果,對獲得的結果進處理,首選會充分利用CPU的多核資源,其次是將會阻塞的任務提早進行提交處理,儘可能減小系統卡頓。(在互聯網公司這樣網絡訪問的方式很是常見。) 那麼今天咱們來談一種新的實現方式Future<V>接口Callable<V>接口java

Future<V>接口Callable<V>網絡

在Future接口中聲明瞭5個方法,下面依次解釋每一個方法的做用:併發

  1. cancel方法用來取消任務,若是取消任務成功則返回true,若是取消任務失敗則返回false。參數mayInterruptIfRunning表示是否容許取消正在執行卻沒有執行完畢的任務,若是設置true,則表示能夠取消正在執行過程當中的任務。若是任務已經完成,則不管mayInterruptIfRunning爲true仍是false,此方法確定返回false,即若是取消已經完成的任務會返回false;若是任務正在執行,若mayInterruptIfRunning設置爲true,則返回true,若mayInterruptIfRunning設置爲false,則返回false;若是任務尚未執行,則不管mayInterruptIfRunning爲true仍是false,確定返回true。
  2. isCancelled方法表示任務是否被取消成功,若是在任務正常完成前被取消成功,則返回 true。
  3. isDone方法表示任務是否已經完成,若任務完成,則返回true;
  4. get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
  5. get(long timeout, TimeUnit unit)用來獲取執行結果,若是在指定時間內,還沒獲取到結果,就直接返回null。 在callable接口中聲明瞭1個方法,call()方法,主要用於執行你的操做,並放回操做後的值。

在callable接口中聲明瞭1個方法,call()方法,主要用於執行你的操做,並放回操做後的值。 舉個栗子 Future接口和Callable接口常常配合使用,廢話很少說,上代碼。dom

場景一ide

該場景只是簡單的對Future接口和Callable接口進行使用介紹,注意Future接口的get()方法會阻塞,直到線程池中的線程將數據處理完成,才執行後面的操做。函數

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class FutrueTest
{

	public static void main(String[] args) throws Exception
	{

		ExecutorService executor = Executors.newSingleThreadExecutor();// 單線程,線程池
		Future<Long> myFuturetask = executor.submit(new MyFutureTask());// 提交任務
		System.out.println("get before");
		long result = myFuturetask.get();// 這個地方會阻塞
		System.out.println("get after");
		System.out.println("get result is " + result);
	}
}

class MyFutureTask implements Callable<Long>
{

	[@Override](https://my.oschina.net/u/1162528)
	public Long call() throws Exception
	{
		long result = 1;

		System.out.println("future task start");

		long max = new Random().nextInt(10);

		for (int i = 1; i <= max; i++)//計算階乘
		{
			result *= i;
		}
		TimeUnit.SECONDS.sleep(10);
		System.out.println("future task end");
		return result;
	}
}

執行結果this

get before
future task start
future task end
get after
get result is 720

場景二.net

如今有這樣一個需求,就是一會兒提交不少的FutureTask到線程池中,而後我等待處理的結果,此時我要作的是:哪一個FutureTask先處理完成,我就先處理其獲得的結果。問題的難度在於:每一個提交到線程池中的FutureTask處理的時間都是不同的,我怎麼來的得知那個FutureTask線程先處理完呢? 題外話:仔細想一想:用Join()、CountDownLatch類、CyclicBarrier類是否是感受都不太好實現?考慮了一下直接Runnable接口實現呢,是否是也會很麻煩?!後續會給出對比~ 廢話很少說,直接上代碼:線程

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class FutrueTest
{

	public static void main(String[] args) throws Exception
	{

		ExecutorService executor = Executors.newCachedThreadPool();// 多個線程的線程池
		List<Future<MyResult>> list = new ArrayList<Future<MyResult>>();
		for (int i = 1; i <= 6; i++)
		{
			list.add(executor.submit(new MyFutureTask(String.valueOf(i))));//提交任務
		}

		System.out.println("do your something");
		TimeUnit.SECONDS.sleep(3);

		int rank = 1;
		while (true)//一直訪問,直到task 列表中的task 爲零
		{
			if (list.size() <= 0)
			{
				break;
			}
			Iterator<Future<MyResult>> iterator = list.iterator();
			while (iterator.hasNext())//循環訪問task
			{
				Future<MyResult> f = iterator.next();
				if (f.isDone())//task 是否完成,若是完成則獲取值,若是
				{
					MyResult result = f.get();
					System.out.println("------------------>my rank is " + rank++ + " future task is " + result.name
							+ " result is " + result.result);
					iterator.remove();
				}
			}
		}
	}
}

class MyFutureTask implements Callable<MyResult>
{

	private String name;

	public MyFutureTask(String name)
	{
		this.name = name;
	}

	[@Override](https://my.oschina.net/u/1162528)
	public MyResult call() throws Exception
	{
		long result = 1;

		long max = new Random().nextInt(10);

		for (int i = 1; i <= max; i++)// 計算階乘
		{
			result *= i;
		}
		TimeUnit.SECONDS.sleep(new Random().nextInt(10));
		System.out.println(name + " future task result " + result);

		return new MyResult(name, result);
	}
}

class MyResult
{
	String name;//記錄任務ID
	long result;//結果

	public MyResult(String name, long result)
	{
		super();
		this.name = name;
		this.result = result;
	}

}

運行結果:code

do your something
6 future task result 1
5 future task result 6
------------------>my rank is 1future task is 5 result is 6
------------------>my rank is 2future task is 6 result is 1
2 future task result 40320
------------------>my rank is 3future task is 2 result is 40320
1 future task result 6
4 future task result 720
------------------>my rank is 4future task is 1 result is 6
------------------>my rank is 5future task is 4 result is 720
3 future task result 24
------------------>my rank is 6future task is 3 result is 24

這個地方主要注意這個isDown()這個函數來判斷提交的Future任務是否執行完成,若是完成就獲取任務結果作後續的處理。 可是咱們發現,存在一個問題就是:並無真的像我想一想的那樣,哪一個任務先執行完,我就先處理哪一個,難道就真的沒有辦法了麼?! 看下面代碼:

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class FutrueTest
{

	public static void main(String[] args) throws Exception
	{

		ExecutorService executor = Executors.newCachedThreadPool();// 多個線程的線程池
		CompletionService<MyResult> completionService = new ExecutorCompletionService<MyResult>(executor);//主要是這個類

		for (int i = 1; i <= 6; i++)
		{
			completionService.submit(new MyFutureTask(String.valueOf(i)));// 提交任務
		}
		System.out.println("do your something");
		TimeUnit.SECONDS.sleep(3);//休眠,也能夠不休眠
		int rank = 0;
		for (int i = 1; i <= 6; i++)
		{

			MyResult result = completionService.take().get();// 會阻塞哦

			System.out.println("------------------>my rank is " + rank++ + " future task is " + result.name
					+ " result is " + result.result);
		}

	}
}

class MyFutureTask implements Callable<MyResult>
{

	private String name;

	public MyFutureTask(String name)
	{
		this.name = name;
	}

	[@Override](https://my.oschina.net/u/1162528)
	public MyResult call() throws Exception
	{
		long result = 1;

		long max = new Random().nextInt(10);

		for (int i = 1; i <= max; i++)// 計算階乘
		{
			result *= i;
		}
		TimeUnit.SECONDS.sleep(new Random().nextInt(10));
		System.out.println(name + " future task result " + result);

		return new MyResult(name, result);
	}
}

class MyResult
{
	String name;// 記錄任務ID
	long result;// 結果

	public MyResult(String name, long result)
	{
		super();
		this.name = name;
		this.result = result;
	}

}

運行結果

do your something
3 future task result 24
1 future task result 6
4 future task result 1
------------------>my rank is 0 future task is 3 result is 24
------------------>my rank is 1 future task is 1 result is 6
------------------>my rank is 2 future task is 4 result is 1
5 future task result 720
6 future task result 40320
------------------>my rank is 3 future task is 5 result is 720
------------------>my rank is 4 future task is 6 result is 40320
2 future task result 2
------------------>my rank is 5 future task is 2 result is 2

終於獲得了咱們想要的結果。

總結 Callable接口和Runnable接口在某些狀況下均可以提交一個本身的任務給線程池來執行。

區別在於:

1.Callable接口能夠有返回值,而Runnable接口沒有。(固然不是絕對的看你怎麼實現)

2.Callable接口實現對象經過ExecutorService接口的submit()方法提交到線程池,而Runnable接口實現對象經過Executor接口的execute()方法提交大線程池,固然ExecutorService接口繼承Executor接口

至於他們的使用,看場景

相關文章
相關標籤/搜索