《Java核心技術(卷1)》筆記:第12章 併發
執行緒
-
(P 552)多程序和多執行緒的本質區別:每一個程序都擁有自己的一整套變數,而執行緒共享資料
-
(P 555)執行緒具有6種狀態:
- New(新建):使用
new
操作符建立執行緒時 - Runnable(可執行):呼叫
start
方法 - Blocked(阻塞)
- Waiting(等待)
- Timed waiting(計時等待)
- Terminated(終止):
run
方法正常退出、沒有捕獲異常
- New(新建):使用
-
(P 558)
interrupt
方法用來請求終止一個執行緒。當對一個執行緒呼叫interrupt
方法時,就會設定執行緒的中斷狀態。每個執行緒都應該不時地檢查這個標誌,以判斷執行緒是否被中斷 -
(P 559)如果執行緒被阻塞,就無法檢查中斷狀態,因此引入
InterruptedException
sleep
或wait
呼叫阻塞的執行緒上呼叫interrupt
方法時,那個阻塞呼叫將被一個InterrupedtException
異常中斷 -
(P 559)如果設定了中斷狀態,此時倘若呼叫
sleep
方法,它不會休眠。實際上,它會清除中斷狀態並丟擲InterruptedException
。因此,如果迴圈裡呼叫了sleep
,不要檢測中斷狀態,而應當捕獲InterruptedException
異常 -
(P 560)
interrupted
、isInterrupted
以及interrupt
方法的區別:方法 性質 作用 影響 interrupted
Thread
的靜態方法檢查當前執行緒是否被中斷 會清除該執行緒的中斷狀態 isInterrupted
Thread
的例項方法測試執行緒是否被中斷 不會改變中斷狀態 interrupt
Thread
的例項方法向執行緒傳送中斷請求 會設定執行緒的中斷狀態 -
(P 561)守護執行緒:唯一用途是為其他執行緒提供服務,當只剩下守護執行緒時,虛擬機器就會退出。可通過
setDaemon
方法將執行緒設為守護執行緒 -
(P 561)執行緒的
run
方法不能丟擲任何檢查型異常,線上程死亡之前,異常會傳遞到一個用於處理未捕獲異常的處理器(必須實現Thread.UncaughtExceptionHandler
介面)方法 性質 作用 setUncaughtExceptionHandler
Thread
的例項方法為任何執行緒安裝一個處理器 setDefaultUncaughtExceptionHandler
Thread
的靜態方法為所有執行緒安裝一個預設的處理器
同步
-
(P 568)Java提供的兩種可防止併發訪問程式碼塊的機制:
-
synchronized
關鍵字 -
ReentrantLock
類(重入鎖)myLock.lock(); // 一個ReentrantLock物件 try { ... } finally { myLock.unlock(); // 必須放在finally裡,不能使用try-with-resources }
-
-
(P 570)重入(reentrant)鎖:執行緒可以反覆獲得已擁有的鎖,被一個鎖保護的程式碼可以呼叫另一個使用相同鎖的方法。注意確保臨界區中的程式碼不要因為丟擲異常而跳出臨界區
-
(P 572)一個鎖物件可以有一個或多個相關聯的條件物件,可以使用
newCondition
方法獲得一個條件物件。方法 性質 作用 newCondition
ReentrantLock
的例項方法獲得一個條件物件 await
Condition
的例項方法當前執行緒現在暫停,並放棄鎖 signalAll
Condition
的例項方法解除等待這個條件的所有執行緒的阻塞狀態 signal
Condition
的例項方法隨機選擇一個執行緒解除其阻塞狀態 使用形式:
class A { private var lock = new ReentrantLock(); private Condition condition; ... private A() { ... condition = lock.newCondition(); } private someMethod() { lock.lock(); try { ... while(!(OK to proceed)) { // await呼叫通常放在迴圈中 condition.await(); } ... condition.signalAll(); // signalAll只是通知等待的執行緒:現在有可能滿足條件,值得再次檢查條件 // 只要一個物件的狀態有變化,而且可能有利於等待的執行緒,就可以呼叫signalAll // signalAll只是解除等待執行緒的阻塞,使這些執行緒可以在當前執行緒釋放鎖之後競爭訪問物件 } finally { lock.unlock(); } } ... }
-
(P 576)Java中的每個物件都有一個內部鎖(只有一個關聯條件)。如果一個方法宣告時有
synchronized
關鍵字,那麼物件的鎖將保護整個方法方法 性質 作用 等價於 wait
Object
的例項方法將一個執行緒增加到等待集中 await
notify
/notifyAll
Object
的例項方法解除等待執行緒的阻塞 signal
/signalAll
-
(P 577)將靜態方法宣告為同步也是合法的,如果呼叫這樣一個方法,它會獲得相關類物件(
Class
物件)的內部鎖 -
(P 577)內部鎖和條件存在一些限制:
- 不能中斷一個正在嘗試獲得鎖的執行緒
- 不能指定嘗試獲得鎖時的超時時間
- 每個鎖僅有一個條件可能是不夠的
-
(P 579)同步塊:
synchronized(obj) { // 會獲得obj物件的鎖 ... }
-
(P 580)監視器的特性:
- 監視器是隻包含私有欄位的類
- 監視器類的每個物件有一個關聯的鎖
- 所有方法有這個鎖鎖定
- 鎖可以有任意多個相關聯的條件
-
(P 581)
volatile
關鍵字為例項欄位的同步訪問提供了一種免鎖機制,volatile
變數不能提供原子性- 另一種安全訪問共享欄位的情況:將欄位宣告為
final
- 另一種安全訪問共享欄位的情況:將欄位宣告為
-
(P 582)
java.util.concurrent.atomic
包中有很多類使用了很高效的機器級指令來保證其他操作的原子性 -
(P 586)執行緒區域性變數:
ThreadLocal
public static final ThreadLocal<SimpleDateFormat> dataFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); // 在一個給定執行緒中首次呼叫get時,會呼叫構造器中的lambda表示式 // 在此之後,get方法會返回屬於當前執行緒的那個例項 String dateStamp = dataFormat.get().format(new Date());
執行緒安全的集合
-
(P 589)阻塞佇列(blocking queue):
add
、element
、offer
、peek
、poll
、put
、remove
、take
-
(P 595)高效的對映、集和佇列:
java.util.concurrent
包提供了ConcurrentHashMap
、ConcurrentSkipListMap
、ConcurrentSkipListSet
、ConcurrentLinkedQueue
-
(P 602)同步包裝器(synchronization wrapper):任何集合類都可以通過使用同步包裝器變成執行緒安全的
List<E> synchArrayList = Collections.synchronizedList(new ArrayList<E>()); Map<K, V> synchHashMap = Collections.synchronizedMap(new HashMap<K, V>());
執行緒池
-
(P 603)
Callable
與Runnable
類似,但是有返回值,只有一個call
方法 -
(P 604)
Future
儲存非同步計算的結果 -
(P 604)執行
Callable
的一種方法是使用FutureTask
,它實現了Future
和Runnable
介面Callable<Integer> task = ...; var futureTask = new FutureTask<Integer>(task); var t = new Thread(futureTask); // it's a Runnable t.start(); ... Integer result = futureTask.get(); // it's a Future
-
(P 605)執行器(
Executors
)類有許多靜態工廠方法,用來構造執行緒池 -
(P 606)使用執行緒池時所做的工作:
- 呼叫
Executors
類的靜態方法newCachedThreadPool
或newFixedThreadPool
- 呼叫
submit
提交Runnable
或Callable
物件 - 儲存好返回的
Future
物件,以便得到結果或者取消任務 - 當不想再提交任何任務時,呼叫
shutdown
- 呼叫
-
(P 607)控制任務組
方法 性質 作用 備註 invokeAny
ExecutorService
的例項方法提交一個 Callable
物件集合中的所有物件,並返回某個已完成任務的結果invokeAll
ExecutorService
的例項方法提交一個 Callable
物件集合中的所有物件,並返回表示所有任務答案的一個Future
物件列表這個方法會阻塞,直到所有任務都完成 -
(P 612)fork-join框架:專門用來支援計算密集型任務,假設有一個處理任務,它可以很自然地分解為子任務
非同步計算
- (P 615)
CompletableFuture
類實現了Future
介面,它提供了獲得結果的另一種機制。你要註冊一個回撥,一旦結果可用,就會(在某個執行緒中)利用該結果呼叫這個回撥(與之不同的是,Future
中的get
方法會阻塞) - (P 615)
Supplier<T>
與Callable<T>
:都描述了無引數而且返回值型別為T
的函式,不過Supplier
函式不能丟擲檢查型異常
程序
- (P 628)
Process
類在一個單獨的作業系統程序中執行一個命令,允許我們與標準輸入、輸出和錯誤流互動。ProcessBuilder
類則允許我們配置Process
物件 - (P 631)
ProcessHandle
介面:要獲得程式啟動的一個程序的更多資訊,或者想更多地瞭解你的計算機上正在執行的任何其他程序,可以使用ProcessHandle
介面 - (P 631)得到
ProcessHandle
的4種方式:- 給定一個
Process
物件p
,p.toHandle()
會生成它的ProcessHandle
- 給定一個
long
型別的作業系統程序ID
,ProcessHandle.of(id)
可以生成這個程序的控制代碼 Process.current()
是執行這個java虛擬機器的程序控制代碼ProcessHandle.allProcesses()
可以生成對當前程序可見的所有作業系統程序的Stream<ProcessHandle>
- 給定一個