隨筆-2  評論-0  文章-20  trackbacks-0

          Java 并發核心編程

          內容涉及:

          1、關于java并發

          2、概念

          3、保護共享數據

          4、并發集合類

          5線程

          6、線程協作及其他

           

          1、關于java并發

          自從java創建以來就已經支持并發的理念,如線程和鎖。這篇指南主要是為幫助java多線程開發人員理解并發的核心概念以及如何應用這些理念。本文的主題是關于具有java語言風格Thread、synchronizedvolatile,以及J2SE5中新增的概念,如鎖(Lock)、原子性(Atomics)、并發集合類、線程協作摘要Executors。開發者通過這些基礎的接口可以構建高并發、線程安全的java應用程序。

           

          2、概念

          本部分描述java并發概念在這篇DZone Refard會被通篇使用。

          JVM并發看CPU內存指令重排序(Memory Reordering):http://kenwublog.com/illustrate-memory-reordering-in-cpu

          java內存模型詳解http://kenwublog.com/explain-java-memory-model-in-detail

          概念

          描述

          Java Memory Model

          Java內存模型

          JavaSE5JSR133)中定義的Java Memory ModelJMM)是為了確保當編寫并發代碼的時候能夠提供Java程序員一個可用的JVM實現。術語JMM的作用類似與一個觀察同步讀寫字段的monitor。它按照“happens-before order(先行發生排序)”的順序—可以解釋為什么一個線程可以獲得其他線程的結果,這組成了一個屬性同步的程序,使字段具有不變性,以及其他屬性。

          monitor

          Monitor

          Java語言中,每個對象都擁有一個訪問代碼關鍵部分并防止其他對象訪問這段代碼的“monitor”(每個對象都擁有一個對代碼關鍵部分提供訪問互斥功能的“monitor”)。這段關鍵部分是使用synchronized對方法或者代碼標注實現的。同一時間在同一個monitor中,只允許一個線程運行代碼的任意關鍵部分。當一個線程試圖獲取代碼的關鍵部分時,如果這段代碼的monitor被其他線程擁有,那么這個線程會無限期的等待這個monitor直到它被其他線程釋放。除了訪問互斥之外,monitor還可以通過waitnotify來實現協作。

          原子字段賦值

          Atomic field assignment

          除了doubleslongs之外的類型,給一個這些類型的字段賦值是一個原子操作。在JVM中,doubleslongs的更新是被實現為2個獨立的操作,因此理論上可能會有其他的線程得到一個部分更新的結果。為了保護共享的doubleslongs,可以使用volatile標記這個字段或者在synchronized修飾的代碼塊中操作字段。

          競爭狀態

          Race condition

          競爭發生當不少于一個線程對一個共享的資源進行一系列的操作,如果這些線程的操作的順序不同,會導致多種可能的結果。

          數據競爭

          Data race

          數據競爭主要發生在多個線程訪問一個共享的、non-final、non-volatile、沒有合適的synchronization限制的字段。Java內存模型不會對這種非同步的數據訪問提供任何的保證。在不同的架構和機器中數據競爭會導致不可預測的行為。

          安全發布

          Safe publications

          在一個對象創建完成之前就發布它的引用時非常危險的。避免這種使用這種引用的一種方法就是在創建期間注冊一個回調接口。另外一種不安全的情況就是在構造子中啟動一個線程。在這2種情況中,非完全創建的對象對于其他線程來說都是可見的。

          不可變字段

          Final Fields

          不可變字段在對象創建之后必須明確設定一個值,否則編譯器就會報出一個錯誤。一旦設定值后,不可變字段的值就不可以再次改變。將一個對象的引用設定為不可變字段并不能阻止這個對象的改變。例如,ArrayList類型的不可變字段不能改變為其他ArrayList實例的引用,但是可以在這個list實例中添加或者刪除對象。

              在創建結尾,對象會遇到final field freeze:如果對象被安全的發布后,即使在沒有synchronization關鍵字修飾的情況下,也能保證所有的線程獲取final字段在構建過程中設定的值。final field freezer不僅對final字段有用,而且作用于final對象中的可訪問屬性。

          不可變對象

          Immutable objects

          在語法上final 字段能夠創建不需要synchronization修飾的、能夠被共享讀取的線程安全的不可變對象。實現Immutable Object需要保證如下條件:

          ·對象被安全的發布(在創建過程中this 引用是無法避免的)

          ·所有字段被聲明為final

          ·在創建之后,在對象字段能夠被訪問的范圍中是不允許修改這個字段的。

          ·class被聲明為final(為了防止subclass違反這些規則)

           

           

          3、保護共享數據

          編寫線程安全的java程序,當修改共享數據的時候要求開發人員使用合適的鎖來保護數據。鎖能夠建立符合Java Memory Model要求的訪問順序,而且確保其他線程知道數據的變化。

          注意:

          Java Memory Model,如果沒有被synchronization修飾,改變數據不需要什么特別的語法表示。JVM能夠自由地重置指令順序的特性和對可見性的限制方式很容易讓開發人員感到奇怪。

           

          3.1Synchronized

          每個對象實例都擁有一個每次只能讓一個線程鎖住的monitor。synchronized能夠用在一個方法或者代碼塊中來鎖住這個monitor。用synchronized修飾一個對象,當修改這個對象的一個字段,synchronized保證其他線程余下的對這個對象的讀操作能夠獲取修改后的值。需要注意的是修改同步塊之外的數據或者synchronized沒有修飾當前被修改的對象,那么不能保證其他線程讀到這些最新的數據。synchronized關鍵字能夠修飾一個對象實例中的函數或者代碼塊。在一個非靜態方法中this關鍵字表示當前的實例對象。在一個synchronized修飾的靜態的方法中,這個方法所在的類使用Class作為實例對象。

           

          3.2、Lock

          Java.util.concurrent.locks包中有個標準Lock接口。ReentrantLock 實現了Lock接口,它完全擁有synchronized的特性,同時還提供了新的功能:獲取Lock的狀態、非阻塞獲取鎖的方法tryLock()、可中斷Lock。

          下面是使用ReentrantLock的詳細示例:

          public class Counter{

          private final Lock lock = new ReentrantLock();

          private int value;

          public int increment() {

          lock.lock();

          try {

          return ++value;

          }finally{

          lock.unlock();

          }

          }

          }

           

           

          3.3、ReadWriteLock

          Java.util.concurrent.locks包中還有個ReadWriteLock接口(實現類是ReentrantWriteReadLock),它定義一對鎖:讀鎖和寫鎖,特征是能夠被并發的讀取但每次只能有一個寫操作。使用ReentrantReadWriteLock并發讀取特性的詳細示例:

          public class ReadWrite {

          private final ReadWriteLock lock = new ReentrantReadWriteLock();

          private int value;

          public void increment(){

          lock.writeLock().lock();

          try{

          value++;

          }finally{

          lock.writeLock().unlock();

          }

          }

          public int current(){

          lock.readLock().lock();

          try{

          return value;

          }finally{

          lock.readLock().unlock();

          }

          }

          }

           

          3.4、volatile

          volatile原理與技巧http://kenwublog.com/the-theory-of-volatile

          volatile修飾符用來標注一個字段,表明任何對這個字段的修改都必須能被其他隨后訪問的線程獲取到,這個修飾符和同步無關。因此,volatile修飾的數據的可見性和synchronization類似,但是這個它只作用于對字段的讀或寫操作。在JavaSE5之前,因為JVM的架構和實現的原因,不同JVMvolatile效果是不同的而且也是不可信的。下面是Java內存模型明確地定義volatile的行為:

          public class Processor implements Runnable {

          private volatile boolean stop;

          public void stopProcessing(){

          stop = true;

          }

          public void run() {

          while (!stop) {

          //do processing

          }

          }

          }

          注意:使用volatile修飾一個數組并不能讓這個數組的每個元素擁有volatile特性,這種聲明只是讓這個數組的reference具有volatile屬性。數組被聲明為AtomicIntegerArray類型,則能夠擁有類似volatile的特性。

          3.5、原子類

          使用volatile的一個缺點是它能夠保證數據的可見性,卻不能在一個原子操作中對volatile修飾的字段同時進行校驗和更新操作。java.util.concurrent.atomic包中有一系列支持在單個非鎖定(lock)的變量上進行原子操作的類,類似于volatile。示例:

          public class Counter{

          private AtomicInteger value = new AtomicInteger();

          private int value;

          public int increment() {

          return value.incrementAndGet();

          }

          }

          incrementAndGet方法是原子類的復合操作的一個示例。booleans, integers, longs, object references, integers數組, longs數組, object references數組 都有相應的原子類。

           

          3.6、ThreadLocal

          通過ThreadLocal能數據保存在一個線程中,而且不需要lock同步。理論上ThreadLocal可以讓一個變量在每個線程都有一個副本。ThreadLocal常用來屏蔽線程的私有變量,例如“并發事務”或者其他的資源。而且,它還被用來維護每個線程的計數器,統計,或者ID生成器。

          public class TransactionManager {

          private static final ThreadLocal<Transaction> currentTransaction 

          = new ThreadLocal<Transaction>() {

          @Override

          protected Transaction initialValue() {

          return new NullTransaction();

          }

          };

          public Transaction currentTransaction() {

          Transaction current = currentTransaction.get();

          if(current.isNull()) {

          current = new TransactionImpl();

          currentTransaction.put(current);

          }

          return current;

          }

          }

           

           

          4Concurrent Collections(并發集合類)

          保護共享數據的一個關鍵技術是在存儲數據的類中封裝同步機制。所有對數據的使用都要經過同步機制的確認使這個技術能夠避免數據的不當訪問。在java.util.concurrent包中有很多為并發使用情況下設計的數據結構。通常,使用這些數據結構比使用同步包裝器裝飾的非同步的集合的效率更高。

          4.1、Concurrent lists and sets

          Table2 中列出了java.util.concurrent包中擁有的3個并發的ListSet實現類。

          描述

          CopyOnWriteArraySet

          CopyOnWriteArraySet在語意上提供寫時復制(copy-on-werite)的特性,對這個集合的每次修改都需要對當前數據結構新建一個副本,因此寫操作發費很大。在迭代器創建的時候,會對當前數據數據結構創建一個快照用于迭代。

          CopyOnWriteArrayList

          CopyOnWriteArrayListCopyOnWriteArraySet類似,也是基于copy-on-write語義實現了List接口

          ConcurrentSkipListSet

          ConcurrentSkipListSet(在JavaSE 6新增的)提供的功能類似于TreeSet,能夠并發的訪問有序的set。因為ConcurrentSkipListSet是基于“跳躍列表(skip list)”實現的,只要多個線程沒有同時修改集合的同一個部分,那么在正常讀、寫集合的操作中不會出現競爭現象。

          skip list: http://blog.csdn.net/yuanyufei/archive/2007/02/14/1509937.aspx

          http://zh.wikipedia.org/zh-cn/%E8%B7%B3%E8%B7%83%E5%88%97%E8%A1%A8

           

          4.2Concurrent maps

          Java.util.concurrent包中有個繼承Map接口的ConcurrentMap的接口,ConcurrentMap提供了一些新的方法(表3)。所有的這些方法在一個原子操作中各自提供了一套操作步驟。如果將每套步驟在放在map之外單獨實現,在非原子操作的多線程訪問的情況下會導致資源競爭。

          3ConcurrentMap的方法:

          方法

          描述

          putIfAbsent(K key, V value) : V

          如果keymap中不存在,則把key-value鍵值對放入map中,否則不執行任何操作。返回值為原來的value,如果key不存在map中則返回null

          removeObject key, Object value) : boolean

          如果map中有這個key及相應的value,那么移除這對數據,否則不執行任何操作

          replace (K key, V value) : V

          如果map中有這個key,那么用新的value替換原來的value,否則不執行任何操作

          replace (K key, V oldValue, V newValue) : boolean

          如果map中有這對key-oldValue數據,那么用newValue替換原來的oldValue,否則不執行任何操作

          在表4中列出的是ConcurrentMap2個實現類

          方法

          描述

          ConcurrentHashMap

          ConcurrentHashMap提供了2種級別的內部哈希方法。第一種級別是選擇一個內部的Segment,第二種是在選定的Segment中將數據哈希到buckets中。第一種方法通過并行地在不同的Segment上進行讀寫操作來實現并發。(ConcurrentHashMap是引入了Segment,每個Segment又是一個hash,ConcurrentHashMap相當于是兩級Hash表,然后鎖是在Segment一級進行的,提高了并發性。http://mooncui.javaeye.com/blog/380884

          http://www.javaeye.com/topic/344876
          )

          ConcurrentSkipListMap

          ConcurrentSkipListMapJavaSE 6新增的類)功能類似TreeMap,是能夠被并發訪問的排序map。盡管能夠被多線程正常的讀寫---只要這些線程沒有同時修改map的同一個部分,ConcurrentSkipListMap的性能指標和TreeMap差不多。

           

          4.3、Queues

          Queues類似于溝通“生產者”和“消費者”的管道。組件從管道的一端放入,然后從另一端取出:“先進先出”(FIFO)的順序。Queue接口在JavaSE5新添加到java.util中的,能夠被用于單線程訪問的場景中,主要適用于多個生產者、一個或多個消費者的情景,所有的讀寫操作都是基于同一個隊列。

          java.util.concurrent包中的BlockingQueue接口是Queue的子接口,而且還添加了新的特性處理如下場景:隊列滿(此時剛好有一個生產者要加入一個新的組件)、隊列空(此時剛好有一個消費者讀取或者刪除一個組件)。BlockingQueue提供如下方案解決這些情況:一直阻塞等待直到其他線程修改隊列的數據狀態;阻塞一段時間之后返回,如果在這段時間內有其他線程修改隊列數據,那么也會返回。

          5QueueBlockingQueue的方法:

          方法

          策略

          插入

          移除

          核查

          Queue

          拋出異常

          add

          remove

          element

          返回特定的值

          offer

          poll

          peek

          Blocking Queue

          一直阻塞

          put

          take

          n/a

          超時阻塞

          offer

          poll

          n/a

          JDK中提供了一些Queue的實現,在表6中是這些實現類的關系列表。

          方法

          描述

          PriorityQueue

          PriorityQueue是唯一一個非線程安全的隊列實現類,用于單線程存放數據并且將數據排序。

          CurrentLinkedQueue

          一個無界的、基于鏈接列表的、唯一一個線程安全的隊列實現類,不支持BlockingQueue。

          ArrayBlockingQueue

          一個有界的、基于數組的阻塞隊列。

          LinkedBlockingQueue

          一個有界的、基于鏈接列表的阻塞隊列。有可能是最常用的隊列實現。

          PriorityBlockingQueue

          一個無界的、基于堆的阻塞隊列。隊列根據設置的Comparator(比較器)來確定組件讀取、移除的順序(不是隊列默認的FIFO順序)

          DelayQueue

          一個無界的、延遲元素(每個延遲元素都會有相應的延遲時間值)的阻塞隊列實現。只有在延時期過了之后,元素才能被移除,而且最先被移除的是延時最先到期的元素。

          SynchronousQueue

          一種0容量的隊列實現,生產者添加元素之后必須等待消費者移除后才可以返回,反之依然。如果生產者和消費者2個線程同時訪問,那么參數直接從生產者傳遞到消費者。經常用于線程之間的數據傳輸。

           

          4.4、Deque

          JavaSE6中新增加了兩端都可以添加和刪除的隊列-Deque (發音"deck",not "dick"). Deques不僅可以從一端添加元素,從另一端移除,而且兩端都可以添加和刪除元素。如同BlockingQueueBlockingDeque接口也為阻塞等待和超時等待的特殊情況提供了解決方法。因為Deque繼承QueueBlockingDeque繼承BlockingQueue,下表中的方法都是可以使用的:

          接口

          頭或尾

          策略

          插入

          移除

          核查

          Queue

          Head

          拋出異常

          addFirst

          removeFirst

          getFirst

          返回特定的值

          offerFirst

          pollFirst

          peekFirst

          Tail

          拋出異常

          addLast

          removeLast

          getLast

          返回特定的值

          offerLast

          pollLast

          peekLast

          BlockingQueue

          Head

          一直阻塞

          putFirst

          takeFirst

          n/a

          超時阻塞

          offerFirst

          pollFirst

          n/a

          Tail

          一直阻塞

          putLast

          takeLast

          n/a

          超時阻塞

          offerLast

          pollLast

          n/a

           

          Deque的一個特殊應用場景是只在一個端口進行添加、刪除、檢查操作--堆棧(first-in-last-out順序)。Deque接口提供了stack相同的方法:push(), pop()peek(),這方法和addFirst(), removeFirst(), peekFirst()一一對應,可以把Deque的任何一個實現類當做堆棧使用。表6中是JDKDequeBlockingDeque的實現。注意Deque繼承Queue,BlockingDeque繼承自BlockingQueue

          8Deques

          5、線程

          Java中,java.lang.Thread類是用來代表一個應用或者JVM線程。代碼是在某個線程類的上下文環境中執行的(使用Thread.currentThread()來獲取當前運行的線程)。

          5.1、線程通訊

          線程之間最簡單的通訊方式是一個線程直接調用另一個線程對象的方法。表9中列出的是線程之間可以直接交互的方法。

          9:線程協作方法

          描述

          LinkedList

          這個經常被用到的類在JavaSE6中有了新的改進-實現了Deque接口。在LinkedList中,可以使用標準的Deque方法來添加或者刪除list兩端的元素。LinkedList還可以被當做一個非同步的堆棧,用來替代同步的Stack

          ArrayDeque

          一個非同步的、支持無限隊列長度(根據需要動態擴展隊列的長度)的Deque實現類

          LinkedBlockingDeque

          LinkeBlockingDequeDeque實現中唯一支持并發的、基于鏈接列表、隊列長度可選的類。

          線程方法

          描述

          start

          啟動一個線程實例,并且執行它的run() 方法。

          join

          一直阻塞直到其他線程退出

          interrupt

          中斷其他線程。線程如果在一個方法中被阻塞,會對interrupt操作做出回應,并在這個方法執行的線程中拋出InterruptedException異常;否則線程的中斷狀態被設定。

          stop, suspend, resume, destroy

          這些方法都被廢棄,不應該再使用了。因為線程處理過程中狀態問題會導致危險的操作。相反,應該使用interrupt() 或者 volatile標示來告訴一個線程應該做什么。

           

          5.2"未捕獲異常"處理器

          線程能夠指定一個UncaughtExceptionHandler來接收任何一個導致線程非正常突然終止的未捕獲異常的通知。

           

          5.3、死鎖

          當存在多個線程(最少2個)等待對方占有的資源,就會形成資源循環依賴和線程等待,產生死鎖。最常見的導致死鎖的資源是對象monitor,同時其他阻塞操作(例如wait/notify)也能導致死鎖。

          很多新的JVM能夠檢測Monitor死鎖,并且可以將線程 dump中由信號(中斷信號)、jstack或者其他線程dump工具生成的死鎖原因顯示打印出來。

          除了死鎖,線程之間還會出現饑餓(starvation)和活鎖(livelock). Starvation是因為一個線程長時間占有一個鎖導致其他的線程一直處于等待狀態無法進行下一步操作。Livelock是因為線程發費大量的時間來協調資源的訪問或者檢測避免死鎖導致沒有一個線程真正的干活。

           

          6、線程協作

          6.1、wait/notify

          wait/notify關鍵字適用于一個線程通知另一個線程所需的條件狀態已就緒,最常用于線程在循環中休眠直到獲取特定條件的場景例如,一個線程一直等待直到隊列中有一個組件能夠處理;當組件添加到隊列時,另一個線程能夠通知這個等待的線程。

          waitnotify的經典用法是:

           

          Thread t = new Thread(runnable);

          t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

          public void uncaughtException(Thread t, Throwable    e) {

          // TODO get Logger and log uncaught exception

          }

          });

          t.start();

           

           

          public class Latch {

          private final Object lock = new Object();

          private volatile boolean flag = false;

          public void waitTillChange(){

          synchronized (lock) {

          while(!flag){

          try {

          lock.wait();

          } catch (InterruptedException e) {

          }

          }

          }

          }

          public void change(){

          synchronized (lock) {

          flag = true;

          lock.notifyAll();

          }

          }

          }

           

          在代碼中需要注意的重要地方是:

          wait、notify、notifyAll必須在synchronized修飾的代碼塊中執行,否則會在運行的時候拋出IllegalMonitorStateException異常

          在循環語句wait的時候一定要設定循環的條件--這樣能夠避免wait開始之前,線程所需的條件已經被其他線程提供了卻依然開始此線程wait導致的時間消耗。同時,這種辦法還能夠保證你的代碼不被虛假的信息喚醒。

          總是要保證在調用notifynotifyAll之前,能夠提供符合線程退出等待的條件。否則會出現即使線程接收到通知信息,卻不能退出循環等待的情況。

          6.2Condition

          JavaSE5中新添加了java.util.concurrent.locks.Condition接口。Condition不僅在API中實現了wait/notify語義,而且提供了幾個新的特性,例如:為每個Lock創建多重Condition、可中斷的等待、訪問統計信息等。Condition是通過Lock示例產生的,示例:

           

          public class LatchCondition {

          private final Lock lock = new ReentrantLock();

          private final Condition condition = lock.newCondition();

          private volatile boolean flag = false;

           

          public void waitTillChange(){

          lock.lock();

          try{

          while(!flag){

          try {

          condition.await();

          } catch (InterruptedException e) {

          }

          }

          }finally{

          lock.unlock();

          }

          }

          public void change(){

          lock.lock();

          try{

          flag = true;

          condition.notifyAll();

          }finally{

          lock.unlock();

          }

          }

          }

           

          6.3Coordination classes

          java.util.concurrent包中有幾個類適用于常見的多線程通訊。這幾個協作類適用范圍幾乎涵蓋了使用wait/notifyCondition最常見的場景,而且更安全、更易于使用。

          CyclicBarrier

          CyclicBarrier初始化的時候指定參與者的數量。參與者調用awart()方法進入阻塞狀態直到參與者的個數達到指定數量,此時最后一個到達的線程執行預定的屏障任務,然后釋放所有的線程。屏障可以被重復的重置狀態。常用于協調分組的線程的啟動和停止。

          CountDownLatch 

          需要指定一個計數才能初始化CountDownLatch。線程調用await()方法進入等待狀態知道計數變為0。其他的線程(或者同一個線程)調用countDown()來減少計數。如果計數變為0后是無法被重置的。常用于當確定數目的操作完成后,觸發數量不定的線程。

          Semaphore 

          Semaphore維護一個“許可”集,能夠使用acquire()方法檢測這個“許可”集,在“許可”可用之前Semaphore會阻塞每個acquire訪問。線程能夠調用release()來返回一個許可。當Semaphore只有一個“許可”的時候,可當做一個互斥鎖來使用。

          Exchanger

          線程在Exchangerexchange()方法上進行交互、原子操作的方式交換數據。功能類似于數據可以雙向傳遞的SynchronousQueue加強版。

          7、任務執行

          很多java并發程序需要一個線程池來執行隊列中的任務。在java.util.concurrent包中為這種類型的任務管理提供了一種可靠的基本方法。

          7.1ExecutorService

          Executor和易擴展的ExecutorService接口規定了用于執行任務的組件的標準。這些接口的使用者可以通過一個標準的接口使用各種具有不同行為的實現類。

           

          最通用的Executor接口只能訪問這種類型的可執行(Runnable)任務 :

          void execute(Runnable command)

          Executor子接口ExecutorService新加了方法,能夠執行:Runnable任務、Callable任務以及任務集合。

          Future<?> submit(Runnable task)

          Future<T> submit(Callable<T> task)

          Future<T> submit(Runnable task, T result)

          List<Future<T>> invokeAll (Collection<? extends Callable<T>> tasks)

          List<Future<T>> invokeAll (Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

          T invokeAny(Collection<? extends Callable<T>> tasks)

          T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

          7.2、Callable and Future

          Callable類似于Runnable,而且能夠返回值、拋出異常:

          V call() throws Exception;

          在一個任務執行框架中提交一個Callable 任務,然后返回一個Future結果是很常見的。Future表示在將來的某個時刻能夠獲取到結果。Future提供能夠獲取結果或者阻塞直到結果可用的方法。任務運行之前或正在運行的時候,可以通過Future中的方法取消。

          如果只是需要一個Runnable特性的Future(例如在Executor執行),可用使用FutureTaskFutureTask實現了FutureRunnable接口,可用提交一個Runnable類型任務,然后在調用部分使用這個Future類型的任務。

          7.3、實現ExecutorService

          ExecutorService最主要的實現類是ThreadPoolExecutor。這個實現類提供了大量的可配置特性:

          線程池--設定常用線程數量(啟動前可選參數)和最大可用線程數量。

          線程工廠--通過自定義的線程工廠生成線程,例如生成自定義線程名的線程。

          工作隊列--指定隊列的實現類,實現類必須是阻塞的、可以是無界的或有界的。

          被拒絕的任務--當隊列已經滿了或者是執行者不可用,需要為這些情況指定解決策略。

          生命周期中的鉤子--重寫擴展在任務運行之前或之后的生命周期中的關鍵點

          關閉--停止已接受的任務,等待正在運行的任務完成后,關閉ThreadPoolExecutor。

          ScheduledThreadPoolExecutorThreadPoolExecutor的一個子類,能夠按照定時的方式完成任務(而不是FIFO方式)。在java.util.Timer不是足夠完善的情況下,ScheduleThreadPoolExecutor具有強大的可適用性。

           

          Executors類有很多靜態方法(表10)用于創建適用于各種常見情況的預先包裝的ExecutorServiceScheduleExecutorService實例

          10

          方法

          描述

          newSingleThreadExecutor

          創建只有一個線程的ExecutorService

          newFixedThreadPool

          返回擁有固定數量線程的ExecutorService

          newCachedThreadPool

          返回一個線程數量可變的ExecutorService

          newSingleThreadScheduledExecutor

          返回只有一個線程的ScheduledExecutorService

          newScheduledThreadPool

          創建擁有一組核心線程的ScheduledExecutorService

           

          下面的例子是創建一個固定線程池,然后提交一個長期運行的任務:

          在這個示例中提交任務到executor之后,代碼沒有阻塞而是立即返回。在代碼的最后一行調用get()方法會阻塞直到有結果返回。

           

          ExecutorService幾乎涵蓋了所有應該創建線程對象或線程池的情景。在代碼中需要直接創建一個線程的時候,可以考慮通過Executor工廠創建的ExecutorService能否實現相同的目標;這樣做經常更簡單、更靈活。

           

          7.4、CompletionService

          除了常見的線程池和輸入隊列模式,還有一種常見的情況:為后面的處理,每個任務生成的結果必須積累下來。CompletionService接口允許提交CallableRunnable任務,而且還可以從任務隊列中獲取這些結果:(綠色部分和英文版不一樣,已和作者確認,英文版將take()poll()方法混淆了)

          Future<V> take () -- 如果結果存在則獲取,否則直接返回

          Future<V> poll () -- 阻塞直到結果可用

          Future<V> poll (long timeout, TimeUnit unit) -- 阻塞直到timeout時間結束

          ExecutorCompletionServiceCompletionService的標準實現類。在ExecutorCompletionService的構成函數中需要一個Executor,ExecutorCompletionService提供輸入隊列和線程池。

           

          8Hot Tip

          熱門信息:當設置線程池大小的時候,最好是基于當前應用所運行的機器擁有的邏輯處理器的數量。在java中,可用使用Runtime.getRuntime().availableProcessors()獲取這個值。在JVM的生命周期中,可用處理器的數目是可變的。

          9、關于作者

          Alex MillerTerracotta Inc公司Java集群開源產品的技術負責人,曾在BEA SystemMetaMatrix工作,是MetaMatrix的首席架構師。他對Java、并發、分布式系統、查詢語言和軟件設計感興趣。他的tweeter@puredanger,bloghttp://tect.puredanger.com,很喜歡在用戶組會議中發言。在St. Louis,AlexLambda Lounge小組的創建人,Lambda Lounge用戶組是為了學習、動態語言、Strange Loop開發會議而創建的。

           

          10、翻譯后記

          開始閱讀英文版的時候,并沒有覺得文章中有什么晦澀的地方。但是在翻譯之后,才發現將文中的意思清楚地表達出來也是個腦力活,有時一句話能夠懂得意思,卻是很難用漢語表達出來:“只可意會,不可言傳”--這也能解釋我當年高中作文為啥每次只能拿40分(總分60)。在禪宗,師傅教弟子佛理,多靠弟子自身的明悟,故有當頭棒喝、醍醐灌頂之說。做翻譯卻不能這樣,總不能讓讀者對著滿篇的鳥文去琢磨明悟吧,須得直譯、意譯并用,梳理文字。

          翻譯也是一個學習的過程。閱讀本文的時候會無意忽略自己以為不重要的詞句,待到真正翻譯的時候,才發現自己一知半解、一竅不通,就只好Google之,翻譯完成后,也學了些知識,可謂是一箭雙雕。

          個人精力所限,翻譯中難免有不對的地方,望大家予以指正。

           

          11、原文+譯文下載地址

          http://download.csdn.net/source/2805800

          posted on 2011-10-18 20:05 soken 閱讀(1142) 評論(0)  編輯  收藏 所屬分類: Java
          主站蜘蛛池模板: 育儿| 甘谷县| 松滋市| 繁昌县| 桐庐县| 台南县| 南雄市| 洛扎县| 盘锦市| 宁陵县| 祁连县| 靖江市| 平果县| 安徽省| 松阳县| 长泰县| 涞水县| 安平县| 岫岩| 尉犁县| 和静县| 望城县| 安义县| 晋州市| 凤冈县| 莱阳市| 北京市| 剑川县| 奉新县| 富顺县| 鸡西市| 宾阳县| 乌拉特前旗| 武义县| 南岸区| 墨竹工卡县| 桂平市| 奎屯市| 长海县| 敖汉旗| 嵊泗县|