《Java核心技術(卷1)》筆記:第12章 並發
- 2020 年 8 月 9 日
- 筆記
- 《Java核心技術》閱讀筆記, JAVA, 閱讀筆記
執行緒
-
(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>
- 給定一個