多线程之Runnable,Callable,Future,FutureTask
2015-02-03 19:17
387 查看
一: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>
相关文章推荐
- Android进阶——多线程系列之Thread、Runnable、Callable、Future、FutureTask
- 多线程中的Runnable、Callable、Future、FutureTask的作用
- Android(Java)之多线程结果返回——Future 、FutureTask、Callable、Runnable
- java 多线程 executor callable runnable future
- Java线程和多线程(十三)——Callable,Future,FutureTask
- 多线程(九)Callable、Future和FutureTask
- java多线程系列(七)---Callable、Future和FutureTask
- Java的Runnable、Callable、Future、FutureTask。
- Java中的Runnable、Callable、Future、FutureTask的区别与示例
- 理解Android多线程里面三种任务Runnable和Callable和FutureTask的用法
- JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没
- java多线程Runnable、Callable、Executor、Future、FutureTask关系解读
- Java中的Runnable、Callable、Future、FutureTask的区别与示例
- Java中的Callable、Future、FutureTask、Runnable的区别与示例
- Java中的Runnable、Callable、Future、FutureTask的区别与示例
- java 多线程(一)---创建线程的三种方式Thread,Runnable,Callable与Future
- JAVA多线程实现的三种方式(继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程)
- Java多线程——Callable、Future和FutureTask
- Java中的Runnable、Callable、Future、FutureTask的区别和CompletionService的使用场景
- 多线程 Callable Runnable 与Future