多线程之Runnable,Callable,Future,FutureTask

一:Runnable与Callable的区别

1. 需要实现的方法不同

实现Runnable接口需要实现run方法

<span style="font-size:14px;">public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}</span>

实现Callable接口需要实现call方法

<span style="font-size:14px;">public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}</span>

2.  Runnable接口的run()方法没有返回值,而Callable接口的call()方法有返回值

获取Callable的call方法的放回结果是通过Future.get()来获取,Future模式是一种多线程设计模式中(详细可参

考:多线程设计模式读书笔记),即在主线程中可以获取子线程的运行结果,但该结果是异步返回的。主线程通过调

用future.get()来获取子线程的执行结果,如果子线程还没有运行完,则该操作会被阻塞,直到子线程执行完或者抛

出异常。

<span style="font-size:14px;">public V get() throws InterruptedException, ExecutionException {
        await();//阻塞住

        Throwable cause = cause();
        if (cause == null) {
            return getNow();
        }
        throw new ExecutionException(cause);
    }</span>

3. 使用方法不同

实现Runnable接口的任务可以传递给Thread(new Runnable())来执行,也可以通过线程池ExecutorService来使

用; 实现Callable接口的任务只能通过线程池ExecutorService来使用。

下面是一个实现Callable接口来进行算术运算并获取计算结果的例子:

<span style="font-size:14px;">public class CalculThread  {
	
	private int count = 0;
	
	private Task task;
	
	public CalculThread(int count){
		this.task = new Task(this);
		this.count = count;
	}
	
	public void start(){
		ExecutorService exector = Executors.newFixedThreadPool(1);
		Future<Integer> result = exector.submit(task);
		try {
			System.out.println("计算结果:"+result.get());//阻塞直到计算完成,或者发生异常
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
	
	private int calcul(int num) throws InterruptedException{
		int result = 0;
		for(int i = 1; i<=num; i++){
			Thread.sleep(1000);
			result = result + i;
		}
		return result;
	}

	private class Task implements Callable<Integer>{

		private CalculThread thread;
		
		public Task(CalculThread thread){
			this.thread = thread;
		}
		
		@Override
		public Integer call() throws Exception {
			return thread.calcul(thread.count);
		}
		
	}
}</span>

主线程:

<span style="font-size:14px;">public class MainThread {

	public static void main(String[] args) {
		CalculThread thread = new CalculThread(10);
		thread.start(); 
	}
}</span>

二:Future

Future接口是Java标准API的一部分,在java.util.concurrent包中。Future接口是Java多线程设计模式Future模式

的实现,可以进行异步计算并获取计算结果。Future模式的详细介绍可参考文章:多线程设计模式读书笔记

Future接口是一个泛型接口,格式为Future<V>,其中V代表了Future执行任务返回值的类型。

<span style="font-family:FangSong_GB2312;font-size:18px;">public interface Future<V> </span>

Future接口主要方法:

1. boolean cancel(boolean mayInterruptIfRunning)

尝试取消任务的执行,参数指定是立即中断任务的执行还是等待正在执行的任务结束。

2. boolean isCancelled()

任务是否已经取消,任务正常完成前将其取消,返回true

3. boolean isDone()

任务是否已经完成。如果任务正常终止,发生异常或者取消,均返回ture

4. V get() InterruptedException, ExecutionException

等待任务执行结束,然后获取V类型的返回结果。如果在get()过程中线程被异常中断,则抛出

InterruptedException,如果执行出现异常,则抛ExecutionException

5. V get(long timeout, TimeUnit unit)  throws InterruptedException, ExecutionException, 

TimeoutException

与get()功能一样,多了个超时时间。参数timeout指定超时时间,unit指定时间的单位。如果超时,将抛出

TimeoutExecption异常。


Future的使用方法可参考上面关于Callable使用的例子。

三:FutureTask

FutureTask实现了RunnableFuture接口,与Runnable,Future之间的关系如下图:

可以看出FutureTask是一个复合体,它具有Runnable的属性,可以传递给Thread来直接运行,也可以提交给

ExecuteService来执行;又具有Future的属性,可以通过FutureTask 的get()方法来获取执行结果,如果线程没有执

行完成的时候,主线程在获取结果过程中一直阻塞等待。

FutureTask的主要方法

1.public FutureTask(Callable<V> callable) 

Callable接口作为入参构造FutureTask

2.public FutureTask(Runnable runnable, V result) 

Runnable接口和返回结果类型作为入参构造FutureTask

3.protected void set(V v) 

设置线程执行完成的结果到FutureTask中

4. protected void setException(Throwable t) 

如果线程运行异常,则将异常设置到FutureTask中

测试案例

FutureTask使用案例1

任务类
<span style="font-size:14px;">public class Task implements Callable<String> {

	@Override
	public String call() throws Exception {
		return print();
	}

	private String print() throws InterruptedException {
		System.out.println("子线程开始执行计算任务");
		// TODO
		Thread.sleep(5 * 1000);
		return "wrold";
	}
}</span>

主线程:

<span style="font-size:14px;">public class FutureTaskDemo {

	public static void main(String[] args) throws InterruptedException,ExecutionException {
		FutureTask<String> futureTask = new FutureTask<String>(new Task());
		Thread thread = new Thread(futureTask);
		thread.start();
		System.out.println("主线程计算开始...");
		Thread.sleep(2 * 1000);
		// TODO主线程的计算任务
		System.out.println("主线程计算结束...");
		while (!futureTask.isDone()) {
			System.out.println("子线程计算还未完成,请等待...");
			Thread.sleep(500);
		}
		System.out.println("子线程计算完成,计算结果" + futureTask.get());
	}
}</span>

执行结果

主线程计算开始...
子线程开始执行计算任务
主线程计算结束...
子线程计算还未完成,请等待...
子线程计算还未完成,请等待...
子线程计算还未完成,请等待...
子线程计算还未完成,请等待...
子线程计算还未完成,请等待...
子线程计算还未完成,请等待...
子线程计算还未完成,请等待...
子线程计算完成,计算结果wrold

FutureTask使用案例2

该例子展示如何将一个任务分派给多个子线程去去完成,并且最后将各个子线程的运算结果汇总起来

<span style="font-size:14px;">public class ConcurrentCalculator {  
 
   private ExecutorService exec;  
   private int cpuCoreNumber;  
   private List<Future<Long>>tasks=new ArrayList<Future<Long>>();  
 
   class SumCalculator implements Callable<Long>{  
       private int[] numbers;  
       private int start;  
       private int end;  
 
       public SumCalculator(final int[] numbers, int start, int end) {  
           this.numbers=numbers;  
           this.start=start;  
           this.end=end;  
       }  
 
       @Override
       public Long call() throws Exception {  
           Long sum=0l;  
           for (int i=start;i <end;i++) {  
               sum += numbers[i];  
           }  
           return sum;  
       }  
   }  
 
   public ConcurrentCalculator() {  
       cpuCoreNumber=Runtime.getRuntime().availableProcessors();  
       exec=Executors.newFixedThreadPool(cpuCoreNumber);  
   }  
 
   public Long sum(final int[] numbers) {  
       // 根据CPU核心个数拆分任务,创建FutureTask并提交到Executor  
       for (int i=0;i <cpuCoreNumber;i++) {  
           int increment=numbers.length / cpuCoreNumber + 1;  
           int start=increment * i;  
           int end=increment * i + increment;  
           if (end >numbers.length)  
               end=numbers.length;  
           SumCalculator subCalc=new SumCalculator(numbers, start, end);  
           FutureTask<Long>task=new FutureTask<Long>(subCalc);  
           tasks.add(task);  
           if (!exec.isShutdown()) {  
               exec.submit(task);  
           }  
       }  
       return getResult();  
   }  
 
   /** 
    * 迭代每个只任务,获得部分和,相加返回 
    *  
    * @return 
    */ 
   public Long getResult() {  
       Long result=0l;  
       for (Future<Long>task : tasks) {  
           try {  
               // 如果计算未完成则阻塞  
               Long subSum=task.get();  
               result += subSum;  
           }catch (InterruptedException e) {  
               e.printStackTrace();  
           }catch (ExecutionException e) {  
               e.printStackTrace();  
           }  
       }  
       return result;  
   }  
 
   public void close() {  
       exec.shutdown();  
   }  
   
   public static void main(String[] args){
	   int[] numbers=new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};  
	   ConcurrentCalculator calc=new ConcurrentCalculator();  
	   Long sum=calc.sum(numbers);  
	   System.out.println(sum);  
	   calc.close(); 
   }
} </span>




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值