您的位置:首页 > 其它

多线程之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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐