1.Runnable
Runnable是個接口,使用很簡單:
1. 實現該接口並重寫run方法
2. 利用該類的對象創建線程
3. 線程啟動時就會自動調用該對象的run方法
通常在開發中結合ExecutorService使用,將任務的提交與任務的執行解耦開,同時也能更好地利用Executor提供的各種特性
ExecutorService executor = Executors.newCachedThreadPool(); executor.submit(new Runnable() { public void run() { //TODO } }); executor.shutdown();
相對於繼承Thread來創建線程方式,使用Runnable可以讓你的實現類同時實現多個接口,而相對於Callable及Future,Runnable方法並不返回任務執行結果且不能拋出異常
2.Callable
與Runnable不同的是,Callable是個泛型參數化接口,並能返回線程的執行結果,且能在無法正常計算時拋出異常
public interface Callable<V> { V call() throws Exception; }
1. Callable並不像Runnable那樣通過Thread的start方法就能啟動實現類的run方法,所以它通常利用ExecutorService的submit方法去啟動call方法自執行任務,而ExecutorService的submit又返回一個Future類型的結果,因此Callable通常也與Future一起使用
ExecutorService pool = Executors.newCachedThreadPool(); Future<String> future = pool.submit(new Callable{ public void call(){ //TODO } });
或者利用FutureTask封裝Callable再由Thread去啟動(少用)
FutureTask<String> task = new FutureTask(new Callable{ public void call(){ //TODO } }); Thead thread = new Thread(task); thread.start();
2. 通過Executors.callbale(Runnable task,T result)可以執行Runnable並返回"結果",但是這個結果並不是Runnable的執行結果(Runnable的run方法是void類型),而是執行者預定義的結果,這點可以從其實現原理RunnableAdpter源碼看出
public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result);//通過RunnableAdapter實現 } static final class RunnableAdapter<T> implements Callable<T> { final Runnable task; final T result; RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; } public T call() { task.run(); return result; //將傳入的結果的直接返回 } }
Runnable與Callable不同點:
1. Runnable不返回任務執行結果,Callable可返回任務執行結果
2. Callable在任務無法計算結果時拋出異常,而Runnable不能
3. Runnable任務可直接由Thread的start方法或ExecutorService的submit方法去執行
3.Future
Future保存異步計算的結果,可以在我們執行任務時去做其他工作,並提供了以下幾個方法
* cancel(boolean mayInterruptIfRunning):試圖取消執行的任務,參數為true時直接中斷正在執行的任務,否則直到當前任務執行完成,成功取消后返回true,否則返回false
* isCancel():判斷任務是否在正常執行完前被取消的,如果是則返回true
* isDone():判斷任務是否已完成
* get():等待計算結果的返回,如果計算被取消了則拋出
* get(long timeout,TimeUtil unit):設定計算結果的返回時間,如果在規定時間內沒有返回計算結果則拋出TimeOutException
使用Future的好處:
1. 獲取任務的結果,判斷任務是否完成,中斷任務
1. Future的get方法很好的替代的了Thread.join或Thread,join(long millis)
2. Future的get方法可以判斷程序代碼(任務)的執行是否超時,如:
try{ future.get(60,TimeUtil.SECOND); }catch(TimeoutException timeout){ log4j.log("任務越野,將被取消!!"); future.cancel(); }
4.FutureTask
FutureTask實現了RunnableFuture接口,提供了即可以使用Runnable來執行任務,又可以使用Future執行任務並取得結果的構造器,所以可以利用FutureTask去封裝Runnable或Callable對象,之后再submit任務
FutureTask(Callable<V> callable)
FutureTask(Runnable runnable, V result)
5.應用
查找包含某關鍵字的文件個數:每個文件啟動一個線程去查找關鍵字
public class FileSearchTask { public static void main(String[] args) throws ExecutionException, InterruptedException { String path = args[0]; String keyword = args[1]; int c = 0; File[] files = new File(path).listFiles(); ArrayList<Future<Integer>> rs = new ArrayList<>(); for(File file: files){ //每個文件啟動一個task去查找 MatchCount count = new MatchCount(); count.file = file; count.keyword = keyword; FutureTask<Integer> task = new FutureTask(count); rs.add(task); //將任務返回的結果添加到集合中 Thread thread = new Thread(task); thread.start(); } for(Future<Integer> f: rs){ c += f.get(); //迭代返回結果並累加 } System.out.println("包含關鍵字的總文件數為:" + c); } } class MatchCount implements Callable<Integer>{ public File file; public String keyword; private Integer count = 0; public Integer call() throws Exception { //call封裝線程所需做的任務 if(search(file)) count ++; return count; } public boolean search(File file){ boolean founded = false; try(Scanner scanner = new Scanner(new FileInputStream(file))){ while(!founded && scanner.hasNextLine()){ if (scanner.nextLine().contains(keyword)) founded = true; } } catch (FileNotFoundException e) { e.printStackTrace(); } return founded; } }
Java並發編程相關的例子:
https://github.com/MOBIN-F/Thread