1. 程式人生 > >人生不設限,高飛吧~

人生不設限,高飛吧~

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

  一、建立任務

  任務就是一個實現了Runnable介面的類。

  建立的時候實run方法即可。

  二、執行任務

  通過java.util.concurrent.ExecutorService介面物件來執行任務,該介面物件通過工具類java.util.concurrent.Executors的靜態方法來建立。

  Executors此包中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。

  ExecutorService提供了管理終止的方法,以及可為跟蹤一個或多個非同步任務執行狀況而生成 Future 的方法。 可以關閉 ExecutorService,這將導致其停止接受新任務。關閉後,執行程式將最後終止,這時沒有任務在執行,也沒有任務在等待執行,並且無法提交新任務。

  executorService.execute(new TestRunnable());

  1、建立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();

  2、將任務新增到執行緒去執行

  當將一個任務新增到執行緒池中的時候,執行緒池會為每個任務建立一個執行緒,該執行緒會在之後的某個時刻自動執行。

  三、關閉執行服務物件

  executorService.shutdown();

  四、綜合例項

package concurrent;

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

/** 
* Created by IntelliJ IDEA. 

* @author leizhimin 2008-11-25 14:28:59 
*/ 
public class TestCachedThreadPool { 
        public static void 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 { 
        public void 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 

 五、獲取任務的執行的返回值

  在Java5之後,任務分兩類:一類是實現了Runnable介面的類,一類是實現了Callable介面的類。兩者都可以被ExecutorService執行,但是Runnable任務沒有返回值,而Callable任務有返回值。並且Callable的call()方法只能通過ExecutorService的submit(Callable<T> task) 方法來執行,並且返回一個 <T> Future<T>,是表示任務等待完成的 Future.

  public interface Callable<V>返回結果並且可能丟擲異常的任務。實現者定義了一個不帶任何引數的叫做 call 的方法。

  Callable 介面類似於 Runnable,兩者都是為那些其例項可能被另一個執行緒執行的類設計的。但是 Runnable 不會返回結果,並且無法丟擲經過檢查的異常。

  Executors 類包含一些從其他普通形式轉換成 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 
*/ 
public class CallableDemo { 
        public static void 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> { 
        private int 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

一個 ExecutorService,它使用可能的幾個池執行緒之一執行每個提交的任務,通常使用 Executors 工廠方法配置。

執行緒池可以解決兩個不同問題:由於減少了每個任務呼叫的開銷,它們通常可以在執行大量非同步任務時提供增強的效能,並且還可以提供繫結和管理資源(包括執行集合任務時使用的執行緒)的方法。每個 ThreadPoolExecutor 還維護著一些基本的統計資料,如完成的任務數。

為了便於跨大量上下文使用,此類提供了很多可調整的引數和擴充套件掛鉤。但是,強烈建議程式設計師使用較為方便的 Executors 工廠方法Executors.newCachedThreadPool()(無界執行緒池,可以進行自動執行緒回收)、Executors.newFixedThreadPool(int)(固定大小執行緒池)和Executors.newSingleThreadExecutor()(單個後臺執行緒),它們均為大多數使用場景預定義了設定。