一: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. 使用方法不同
<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的主要方法
测试案例
<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
<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>