天天看點

Callable接口解析

1.接口的定義:

public interface Callable<V>   
{   
    V call() throws Exception;   
} 
           

2.Callable和Runnable的異同

先看下Runnable接口的定義

public interface Runnable {
    public abstract void run();
}
           

Callable的call()方法類似于Runnable接口中run()方法,都定義任務要完成的工作,實作這兩個接口時要分别重寫這兩個方法,主要的不同之處是call()方法是有傳回值的(其實還有一些差別,例如call方法可以抛出異常,run方法不可以),運作Callable任務可以拿到一個Future對象,表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,并檢索計算的結果。通過Future對象可以了解任務執行情況,可取消任務的執行,還可擷取執行結果。

3. Callable類型的任務可以有兩種執行方式:

我們先定義一個Callable任務MyCallableTask:

class MyCallableTask implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("線程在進行計算");
        Thread.sleep();
        int sum = ;
        for(int i=;i<;i++)
            sum += i;
        return sum;
    }
}
           

①借助FutureTask執行

FutureTask類同時實作了兩個接口,Future和Runnable接口,是以它既可以作為Runnable被線程執行,又可以作為Future得到Callable的傳回值。

借助FutureTask執行的大體流程是:

Callable<Integer> mycallabletask = new MyCallableTask();  
FutureTask<Integer> futuretask= new FutureTask<Integer>(mycallabletask);  
new Thread(futuretask).start();
           

通過futuretask可以得到MyCallableTask的call()的運作結果:

futuretask.get();

②借助線程池來運作

線程池中執行Callable任務的原型例如:

public interface ExecutorService extends Executor {

  //送出一個Callable任務,傳回值為一個Future類型
  <T> Future<T> submit(Callable<T> task);

  //other methods...
  }
           

借助線程池來運作Callable任務的一般流程為:

ExecutorService exec = Executors.newCachedThreadPool();
  Future<Integer> future = exec.submit(new MyCallableTask());
           

通過future可以得到MyCallableTask的call()的運作結果:

future.get();

在網上看到了幾個比較好的代碼例子:

a.Callable任務借助FutureTask運作:

public class CallableAndFutureTask {
    public static void main(String[] args) {
        Callable<Integer> callable = new Callable<Integer>() {
            public Integer call() throws Exception {
                return new Random().nextInt();
            }
        };
        FutureTask<Integer> future = new FutureTask<Integer>(callable);
        new Thread(future).start();
        try {
            Thread.sleep();
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
           

b.Callable任務和線程池一起使用,然後傳回值是Future:

public class CallableAndFuture {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        Future<Integer> future = threadPool.submit(new Callable<Integer>() {
            public Integer call() throws Exception {
                return new Random().nextInt();
            }
        });
        try {
            Thread.sleep();// 可能做一些事情
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
           

以上a,b例子摘自(http://blog.csdn.net/ghsau/article/details/7451464)

c.當執行多個Callable任務,有多個傳回值時,我們可以建立一個Future的集合,例如:

class MyCallableTask implements Callable<String> {
    private int id;  
    public OneTask(int id){  
        this.id = id;  
    }  
    @Override  
    public String call() throws Exception {  
        for(int i = ;i<;i++){
            System.out.println("Thread"+ id);  
            Thread.sleep(); 
        }  
        return "Result of callable: "+id;  
    }   
}
public class Test {   

    public static void main(String[] args) {  
        //Callable<String> mycallabletask = new MyCallableTask(1);  
        ExecutorService exec = Executors.newCachedThreadPool();    
        ArrayList<Future<String>> results = new ArrayList<Future<String>>();      

        for (int i = ; i < ; i++) {    
            results.add(exec.submit(new MyCallableTask(i)));    
        }    

        for (Future<String> fs : results) {    
            if (fs.isDone()) {    
                try {  
                    System.out.println(fs.get());   
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
            } else {    
                System.out.println("MyCallableTask任務未完成!");    
            }    
        }    
        exec.shutdown();  
    }  
}
           

那麼引入Callable接口具有哪些好處呢?

①可以獲得任務執行傳回值;

②通過與Future的結合,可以實作利用Future來跟蹤異步計算的結果。