ExecutorService中submit和execute的區別(轉)

在Java5以後,併發線程這塊發生了根本的變化,最重要的莫過於新的啓動、調度、管理線程的一大堆API了。在Java5之後,經過Executor來啓動線程比用Thread的start()更好。在新特徵中,能夠很容易控制線程的啓動、執行和關閉過程,還能夠很容易使用線程池的特性。html

 
1、建立任務
 
任務就是一個實現了Runnable接口的類。
建立的時候實run方法便可。
 
2、執行任務
 
經過java.util.concurrent.ExecutorService接口對象來執行任務,該接口對象經過工具類java.util.concurrent.Executors的靜態方法來建立。
 
Executors此包中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。
 
ExecutorService提供了管理終止的方法,以及可爲跟蹤一個或多個異步任務執行情況而生成 Future 的方法。 能夠關閉 ExecutorService,這將致使其中止接受新任務。關閉後,執行程序將最後終止,這時沒有任務在執行,也沒有任務在等待執行,而且沒法提交新任務。
            executorService.execute(new TestRunnable());
 
一、建立ExecutorService
經過工具類java.util.concurrent.Executors的靜態方法來建立。
Executors此包中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。
 
好比,建立一個ExecutorService的實例,ExecutorService其實是一個線程池的管理工具:
        ExecutorService executorService = Executors.newCachedThreadPool();
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        ExecutorService executorService = Executors.newSingleThreadExecutor();
 
二、將任務添加到線程去執行
當將一個任務添加到線程池中的時候,線程池會爲每一個任務建立一個線程,該線程會在以後的某個時刻自動執行。
 
3、關閉執行服務對象
        executorService.shutdown();
 
4、綜合實例
 
package concurrent; 

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

/** 
* Created by IntelliJ IDEA. 

* @author leizhimin 2008-11-25 14:28:59 
*/
publicclass TestCachedThreadPool { 
        publicstaticvoid main(String[] args) { 
//                ExecutorService executorService = Executors.newCachedThreadPool();
                ExecutorService executorService = Executors.newFixedThreadPool(5);
//         ExecutorService executorService = Executors.newSingleThreadExecutor();

                for (int i = 0; i < 5; i++) { 
                        executorService.execute(new TestRunnable()); 
                        System.out.println("************* a" + i + " *************"); 
                } 
                executorService.shutdown(); 
        } 


class TestRunnable implements Runnable { 
        publicvoid run() { 
                System.out.println(Thread.currentThread().getName() + "線程被調用了。"); 
                while (true) { 
                        try { 
                                Thread.sleep(5000); 
                                System.out.println(Thread.currentThread().getName()); 
                        } catch (InterruptedException e) { 
                                e.printStackTrace(); 
                        } 
                } 
        } 
}
 
運行結果:
************* a0 ************* 
************* a1 ************* 
pool-1-thread-2線程被調用了。 
************* a2 ************* 
pool-1-thread-3線程被調用了。 
pool-1-thread-1線程被調用了。 
************* a3 ************* 
************* a4 ************* 
pool-1-thread-4線程被調用了。 
pool-1-thread-5線程被調用了。 
pool-1-thread-2 
pool-1-thread-1 
pool-1-thread-3 
pool-1-thread-5 
pool-1-thread-4 
pool-1-thread-2 
pool-1-thread-1 
pool-1-thread-3 
pool-1-thread-5 
pool-1-thread-4 
     ......
 
 
5、獲取任務的執行的返回值
在Java5以後,任務分兩類:一類是實現了Runnable接口的類,一類是實現了Callable接口的類。二者均可以被ExecutorService執行,可是Runnable任務沒有返回值,而Callable任務有返回值。而且Callable的call()方法只能經過ExecutorService的(<T> task) 方法來執行,而且返回一個 <T><T>,是表示任務等待完成的 Future。
 
public interface  Callable<V>
返回結果而且可能拋出異常的任務。實現者定義了一個不帶任何參數的叫作 call 的方法。
Callable 接口相似於,二者都是爲那些其實例可能被另外一個線程執行的類設計的。可是 Runnable 不會返回結果,而且沒法拋出通過檢查的異常。
類包含一些從其餘普通形式轉換成 Callable 類的實用方法。
 
 
Callable中的call()方法相似Runnable的run()方法,就是前者有返回值,後者沒有。
 
當將一個Callable的對象傳遞給ExecutorService的submit方法,則該call方法自動在一個線程上執行,而且會返回執行結果Future對象。
 
一樣,將Runnable的對象傳遞給ExecutorService的submit方法,則該run方法自動在一個線程上執行,而且會返回執行結果Future對象,可是在該Future對象上調用get方法,將返回null。
 
遺憾的是,在Java API文檔中,這塊介紹的很糊塗,估計是翻譯人員還沒搞清楚的緣故吧。或者說是註釋不到位。下面看個例子:
 
import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.*; 

/** 
* Callable接口測試 

* @author leizhimin 2008-11-26 9:20:13 
*/
publicclass CallableDemo { 
        publicstaticvoid main(String[] args) { 
                ExecutorService executorService = Executors.newCachedThreadPool(); 
                List<Future<String>> resultList = new ArrayList<Future<String>>(); 

                //建立10個任務並執行
                for (int i = 0; i < 10; i++) { 
                        //使用ExecutorService執行Callable類型的任務,並將結果保存在future變量中
                        Future<String> future = executorService.submit(new TaskWithResult(i)); 
                        //將任務執行結果存儲到List中
                        resultList.add(future); 
                } 

                //遍歷任務的結果
                for (Future<String> fs : resultList) { 
                        try { 
                                System.out.println(fs.get());     //打印各個線程(任務)執行的結果
                        } catch (InterruptedException e) { 
                                e.printStackTrace(); 
                        } catch (ExecutionException e) { 
                                e.printStackTrace(); 
                        } finally { 
                                //啓動一次順序關閉,執行之前提交的任務,但不接受新任務。若是已經關閉,則調用沒有其餘做用。
                                executorService.shutdown(); 
                        } 
                } 
        } 



class TaskWithResult implements Callable<String> { 
        privateint id; 

        public TaskWithResult(int id) { 
                this.id = id; 
        } 

        /** 
         * 任務的具體過程,一旦任務傳給ExecutorService的submit方法,則該方法自動在一個線程上執行。 
         * 
         * @return 
         * @throws Exception 
         */
        public String call() throws Exception { 
                System.out.println("call()方法被自動調用,幹活!!!             " + Thread.currentThread().getName()); 
                //一個模擬耗時的操做
                for (int i = 999999; i > 0; i--) ; 
                return"call()方法被自動調用,任務的結果是:" + id + "    " + Thread.currentThread().getName(); 
        } 
}
 
運行結果:
call()方法被自動調用,幹活!!!             pool-1-thread-1 
call()方法被自動調用,幹活!!!             pool-1-thread-3 
call()方法被自動調用,幹活!!!             pool-1-thread-4 
call()方法被自動調用,幹活!!!             pool-1-thread-6 
call()方法被自動調用,幹活!!!             pool-1-thread-2 
call()方法被自動調用,幹活!!!             pool-1-thread-5 
call()方法被自動調用,任務的結果是:0    pool-1-thread-1 
call()方法被自動調用,任務的結果是:1    pool-1-thread-2 
call()方法被自動調用,幹活!!!             pool-1-thread-2 
call()方法被自動調用,幹活!!!             pool-1-thread-6 
call()方法被自動調用,幹活!!!             pool-1-thread-4 
call()方法被自動調用,任務的結果是:2    pool-1-thread-3 
call()方法被自動調用,幹活!!!             pool-1-thread-3 
call()方法被自動調用,任務的結果是:3    pool-1-thread-4 
call()方法被自動調用,任務的結果是:4    pool-1-thread-5 
call()方法被自動調用,任務的結果是:5    pool-1-thread-6 
call()方法被自動調用,任務的結果是:6    pool-1-thread-2 
call()方法被自動調用,任務的結果是:7    pool-1-thread-6 
call()方法被自動調用,任務的結果是:8    pool-1-thread-4 
call()方法被自動調用,任務的結果是:9    pool-1-thread-3 

Process finished with exit code 0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

由於以前一直是用的execute方法,最近有個狀況須要用到submit方法,因此研究了下。java

三個區別:併發

一、接收的參數不同app

二、submit有返回值,而execute沒有dom

Method submit extends base method Executor.execute by creating and returning a Future that can be used to cancel execution and/or wait for completion. 異步

用到返回值的例子,好比說我有不少個作validation的task,我但願全部的task執行完,而後每一個task告訴我它的執行結果,是成功仍是失敗,若是是失敗,緣由是什麼。而後我就能夠把全部失敗的緣由綜合起來發給調用者。ide

我的以爲cancel execution這個用處不大,不多有須要去取消執行的。工具

而最大的用處應該是第二點。學習

三、submit方便Exception處理測試

There is a difference when looking at exception handling. If your tasks throws an exception and if it was submitted with execute this exception will go to the uncaught exception handler (when you don't have provided one explicitly, the default one will just print the stack trace to System.err). If you submitted the task with submit any thrown exception, checked or not, is then part of the task's return status. For a task that was submitted with submit and that terminates with an exception, the Future.get will rethrow this exception, wrapped in an ExecutionException.

意思就是若是你在你的task裏會拋出checked或者unchecked exception,而你又但願外面的調用者可以感知這些exception並作出及時的處理,那麼就須要用到submit,經過捕獲Future.get拋出的異常。

 

好比說,我有不少更新各類數據的task,我但願若是其中一個task失敗,其它的task就不須要執行了。那我就須要catch Future.get拋出的異常,而後終止其它task的執行,代碼以下:

51cto上有一篇很是好的文章「Java5併發學習」(http://lavasoft.blog.51cto.com/62575/115112),下面的代碼是基於它之上修改的。

 

  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. import java.util.Random;  
  4. import java.util.concurrent.Callable;  
  5. import java.util.concurrent.ExecutionException;  
  6. import java.util.concurrent.ExecutorService;  
  7. import java.util.concurrent.Executors;  
  8. import java.util.concurrent.Future;  
  9.   
  10. public class ExecutorServiceTest {  
  11.     public static void main(String[] args) {  
  12.         ExecutorService executorService = Executors.newCachedThreadPool();  
  13.         List<Future<String>> resultList = new ArrayList<Future<String>>();  
  14.   
  15.         // 建立10個任務並執行  
  16.         for (int i = 0; i < 10; i++) {  
  17.             // 使用ExecutorService執行Callable類型的任務,並將結果保存在future變量中  
  18.             Future<String> future = executorService.submit(new TaskWithResult(i));  
  19.             // 將任務執行結果存儲到List中  
  20.             resultList.add(future);  
  21.         }  
  22.         executorService.shutdown();  
  23.   
  24.         // 遍歷任務的結果  
  25.         for (Future<String> fs : resultList) {  
  26.             try {  
  27.                 System.out.println(fs.get()); // 打印各個線程(任務)執行的結果  
  28.             } catch (InterruptedException e) {  
  29.                 e.printStackTrace();  
  30.             } catch (ExecutionException e) {  
  31.                 executorService.shutdownNow();  
  32.                 e.printStackTrace();  
  33.                 return;  
  34.             }  
  35.         }  
  36.     }  
  37. }  
  38.   
  39. class TaskWithResult implements Callable<String> {  
  40.     private int id;  
  41.   
  42.     public TaskWithResult(int id) {  
  43.         this.id = id;  
  44.     }  
  45.   
  46.     /** 
  47.      * 任務的具體過程,一旦任務傳給ExecutorService的submit方法,則該方法自動在一個線程上執行。 
  48.      *  
  49.      * @return 
  50.      * @throws Exception 
  51.      */  
  52.     public String call() throws Exception {  
  53.         System.out.println("call()方法被自動調用,幹活!!!             " + Thread.currentThread().getName());  
  54.         if (new Random().nextBoolean())  
  55.             throw new TaskException("Meet error in task." + Thread.currentThread().getName());  
  56.         // 一個模擬耗時的操做  
  57.         for (int i = 999999999; i > 0; i--)  
  58.             ;  
  59.         return "call()方法被自動調用,任務的結果是:" + id + "    " + Thread.currentThread().getName();  
  60.     }  
  61. }  
  62.   
  63. class TaskException extends Exception {  
  64.     public TaskException(String message) {  
  65.         super(message);  
  66.     }  
  67. }  

執行的結果相似於:

 

  1. call()方法被自動調用,幹活!!!             pool-1-thread-1  
  2. call()方法被自動調用,幹活!!!             pool-1-thread-2  
  3. call()方法被自動調用,幹活!!!             pool-1-thread-3  
  4. call()方法被自動調用,幹活!!!             pool-1-thread-5  
  5. call()方法被自動調用,幹活!!!             pool-1-thread-7  
  6. call()方法被自動調用,幹活!!!             pool-1-thread-4  
  7. call()方法被自動調用,幹活!!!             pool-1-thread-6  
  8. call()方法被自動調用,幹活!!!             pool-1-thread-7  
  9. call()方法被自動調用,幹活!!!             pool-1-thread-5  
  10. call()方法被自動調用,幹活!!!             pool-1-thread-8  
  11. call()方法被自動調用,任務的結果是:0    pool-1-thread-1  
  12. call()方法被自動調用,任務的結果是:1    pool-1-thread-2  
  13. java.util.concurrent.ExecutionException: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3  
  14.     at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)  
  15.     at java.util.concurrent.FutureTask.get(FutureTask.java:83)  
  16.     at com.cicc.pts.ExecutorServiceTest.main(ExecutorServiceTest.java:29)  
  17. Caused by: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3  
  18.     at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:57)  
  19.     at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:1)  
  20.     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)  
  21.     at java.util.concurrent.FutureTask.run(FutureTask.java:138)  
  22.     at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)  
  23.     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)  
  24.     at java.lang.Thread.run(Thread.java:619)  
能夠看見一旦某個task出錯,其它的task就中止執行。
 
轉兄弟的:http://www.cnblogs.com/wanqieddy/p/3853863.html 很給力謝謝、
相關文章
相關標籤/搜索