## Chapter 11. Concurrency(并發)
### Item 81: Prefer concurrency utilities to wait and notify(并發實用工具優于 wait 和 notify)
The first edition of this book devoted an item to the correct use of wait and notify [Bloch01, Item 50]. Its advice is still valid and is summarized at end of this item, but this advice is far less important than it once was. This is because there is far less reason to use wait and notify. Since Java 5, the platform has provided higher-level concurrency utilities that do the sorts of things you formerly had to hand-code atop wait and notify. **Given the difficulty of using wait and notify correctly, you should use the higher-level concurrency utilities instead.**
這本書的第一版專門介紹了 wait 和 notify 的正確用法 [Bloch01, item 50]。這些建議仍然有效,并在本條目末尾作了總結,但這一建議已遠不如從前重要。這是因為使用 wait 和 notify 的理由要少得多。自 Java 5 以來,該平臺提供了更高級別的并發實用工具,可以執行以前必須在 wait 和 notify 上手工編寫代碼的操作。**考慮到正確使用 wait 和 notify 的困難,你應該使用更高級別的并發實用工具。**
The higher-level utilities in java.util.concurrent fall into three categories: the Executor Framework, which was covered briefly in Item 80; concurrent collections; and synchronizers. Concurrent collections and synchronizers are covered briefly in this item.
`java.util.concurrent` 中級別較高的實用工具可分為三類:Executor 框架,[Item-80](/Chapter-11/Chapter-11-Item-80-Prefer-executors,-tasks,-and-streams-to-threads.md) 簡要介紹了該框架;并發集合;同步器。本條目簡要介紹并發集合和同步器。
The concurrent collections are high-performance concurrent implementations of standard collection interfaces such as List, Queue, and Map. To provide high concurrency, these implementations manage their own synchronization internally (Item 79). Therefore, **it is impossible to exclude concurrent activity from a concurrent collection; locking it will only slow the program.**
并發集合是標準集合接口,如 List、Queue 和 Map 的高性能并發實現。為了提供高并發性,這些實現在內部管理它們自己的同步([Item-79](/Chapter-11/Chapter-11-Item-79-Avoid-excessive-synchronization.md))。因此,**不可能從并發集合中排除并發活動;鎖定它只會使程序變慢。**
Because you can’t exclude concurrent activity on concurrent collections, you can’t atomically compose method invocations on them either. Therefore, concurrent collection interfaces were outfitted with state-dependent modify operations, which combine several primitives into a single atomic operation. These operations proved sufficiently useful on concurrent collections that they were added to the corresponding collection interfaces in Java 8, using default methods (Item 21).
因為不能排除并發集合上的并發活動,所以也不能原子地組合對它們的方法調用。因此,并發集合接口配備了依賴于狀態的修改操作,這些操作將多個基本操作組合成單個原子操作。這些操作在并發集合上非常有用,因此使用默認方法([Item-21](/Chapter-4/Chapter-4-Item-21-Design-interfaces-for-posterity.md))將它們添加到 Java 8 中相應的集合接口。
For example, Map’s putIfAbsent(key, value) method inserts a mapping for a key if none was present and returns the previous value associated with the key, or null if there was none. This makes it easy to implement thread-safe canonicalizing maps. This method simulates the behavior of String.intern:
例如,Map 的 `putIfAbsent(key, value)` 方法為一個沒有映射的鍵插入一個映射,并返回與鍵關聯的前一個值,如果沒有,則返回 null。這使得實現線程安全的規范化 Map 變得很容易。這個方法模擬了 `String.intern` 的行為。
```
// Concurrent canonicalizing map atop ConcurrentMap - not optimal
private static final ConcurrentMap<String, String> map =new ConcurrentHashMap<>();
public static String intern(String s) {
String previousValue = map.putIfAbsent(s, s);
return previousValue == null ? s : previousValue;
}
```
In fact, you can do even better. ConcurrentHashMap is optimized for retrieval operations, such as get. Therefore, it is worth invoking get initially and calling putIfAbsent only if get indicates that it is necessary:
事實上,你可以做得更好。ConcurrentHashMap 針對 get 等檢索操作進行了優化。因此,只有在 get 表明有必要時,才值得首先調用 get 再調用 putIfAbsent:
```
// Concurrent canonicalizing map atop ConcurrentMap - faster!
public static String intern(String s) {
String result = map.get(s);
if (result == null) {
result = map.putIfAbsent(s, s);
if (result == null)
result = s;
}
return result;
}
```
Besides offering excellent concurrency, ConcurrentHashMap is very fast. On my machine, the intern method above is over six times faster than String.intern (but keep in mind that String.intern must employ some strategy to keep from leaking memory in a long-lived application). Concurrent collections make synchronized collections largely obsolete. For example, **use ConcurrentHashMap in preference to Collections.synchronizedMap.** Simply replacing synchronized maps with concurrent maps can dramatically increase the performance of concurrent applications.
除了提供優秀的并發性,ConcurrentHashMap 還非常快。在我的機器上,上面的 intern 方法比 `String.intern` 快六倍多(但是請記住,`String.intern` 必須使用一些策略來防止在長時間運行的應用程序中內存泄漏)。并發集合使同步集合在很大程度上過時。例如,**使用 ConcurrentHashMap 而不是 `Collections.synchronizedMap`。** 只要用并發 Map 替換同步 Map 就可以顯著提高并發應用程序的性能。
Some of the collection interfaces were extended with blocking operations, which wait (or block) until they can be successfully performed. For example, BlockingQueue extends Queue and adds several methods, including take, which removes and returns the head element from the queue, waiting if the queue is empty. This allows blocking queues to be used for work queues (also known as producer-consumer queues), to which one or more producer threads enqueue work items and from which one or more consumer threads dequeue and process items as they become available. As you’d expect, most ExecutorService implementations, including ThreadPoolExecutor, use a BlockingQueue (Item 80).
一些集合接口使用阻塞操作進行了擴展,這些操作將等待(或阻塞)成功執行。例如,BlockingQueue 擴展了 Queue 并添加了幾個方法,包括 take,它從隊列中刪除并返回首個元素,如果隊列為空,則等待。這允許將阻塞隊列用于工作隊列(也稱為生產者-消費者隊列),一個或多個生產者線程將工作項添加到該工作隊列中,一個或多個消費者線程將工作項從該工作隊列中取出并在這些工作項可用時處理它們。正如你所期望的,大多數 ExecutorService 實現,包括 ThreadPoolExecutor,都使用 BlockingQueue([Item-80](/Chapter-11/Chapter-11-Item-80-Prefer-executors,-tasks,-and-streams-to-threads.md))。
Synchronizers are objects that enable threads to wait for one another, allowing them to coordinate their activities. The most commonly used synchronizers are CountDownLatch and Semaphore. Less commonly used are CyclicBarrier and Exchanger. The most powerful synchronizer is Phaser.
同步器是允許線程彼此等待的對象,允許它們協調各自的活動。最常用的同步器是 CountDownLatch 和 Semaphore。較不常用的是 CyclicBarrier 和 Exchanger。最強大的同步器是 Phaser。
Countdown latches are single-use barriers that allow one or more threads to wait for one or more other threads to do something. The sole constructor for CountDownLatch takes an int that is the number of times the countDown method must be invoked on the latch before all waiting threads are allowed to proceed.
Countdown latches are single-use barriers,允許一個或多個線程等待一個或多個其他線程執行某些操作。CountDownLatch 的惟一構造函數接受一個 int,這個 int 是在允許所有等待的線程繼續之前,必須在鎖存器上調用倒計時方法的次數。
It is surprisingly easy to build useful things atop this simple primitive. For example, suppose you want to build a simple framework for timing the concurrent execution of an action. This framework consists of a single method that takes an executor to execute the action, a concurrency level representing the number of actions to be executed concurrently, and a runnable representing the action. All of the worker threads ready themselves to run the action before the timer thread starts the clock. When the last worker thread is ready to run the action, the timer thread “fires the starting gun,” allowing the worker threads to perform the action. As soon as the last worker thread finishes performing the action, the timer thread stops the clock. Implementing this logic directly on top of wait and notify would be messy to say the least, but it is surprisingly straightforward on top of CountDownLatch:
在這個簡單的基本類型上構建有用的東西非常容易。例如,假設你想要構建一個簡單的框架來為一個操作的并發執行計時。這個框架由一個方法組成,該方法使用一個 executor 來執行操作,一個并發級別表示要并發執行的操作的數量,一個 runnable 表示操作。所有工作線程都準備在 timer 線程啟動時鐘之前運行操作。當最后一個工作線程準備好運行該操作時,計時器線程「發令槍」,允許工作線程執行該操作。一旦最后一個工作線程完成該操作,計時器線程就停止時鐘。在 wait 和 notify 的基礎上直接實現這種邏輯至少會有點麻煩,但是在 CountDownLatch 的基礎上實現起來卻非常簡單:
```
// Simple framework for timing concurrent execution
public static long time(Executor executor, int concurrency,Runnable action) throws InterruptedException {
CountDownLatch ready = new CountDownLatch(concurrency);
CountDownLatch start = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(concurrency);
for (int i = 0; i < concurrency; i++) {
executor.execute(() -> {
ready.countDown(); // Tell timer we're ready
try {
start.await(); // Wait till peers are ready
action.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
done.countDown(); // Tell timer we're done
}
});
}
ready.await(); // Wait for all workers to be ready
long startNanos = System.nanoTime();
start.countDown(); // And they're off!
done.await(); // Wait for all workers to finish
return System.nanoTime() - startNanos;
}
```
Note that the method uses three countdown latches. The first, ready, is used by worker threads to tell the timer thread when they’re ready. The worker threads then wait on the second latch, which is start. When the last worker thread invokes ready.countDown, the timer thread records the start time and invokes start.countDown, allowing all of the worker threads to proceed. Then the timer thread waits on the third latch, done, until the last of the worker threads finishes running the action and calls done.countDown. As soon as this happens, the timer thread awakens and records the end time.
注意,該方法使用三個倒計時鎖。第一個是 ready,工作線程使用它來告訴 timer 線程它們什么時候準備好了。工作線程然后等待第二個鎖存器,即 start。當最后一個工作線程調用 `ready.countDown` 時。timer 線程記錄開始時間并調用 `start.countDown`,允許所有工作線程繼續。然后計時器線程等待第三個鎖存器 done,直到最后一個工作線程運行完操作并調用 `done.countDown`。一旦發生這種情況,timer 線程就會喚醒并記錄結束時間。
A few more details bear noting. The executor passed to the time method must allow for the creation of at least as many threads as the given concurrency level, or the test will never complete. This is known as a thread starvation deadlock [Goetz06, 8.1.1]. If a worker thread catches an InterruptedException, it reasserts the interrupt using the idiom Thread.currentThread().interrupt() and returns from its run method. This allows the executor to deal with the interrupt as it sees fit. Note that System.nanoTime is used to time the activity. **For interval timing, always use System.nanoTime rather than System.currentTimeMillis.** System.nanoTime is both more accurate and more precise and is unaffected by adjustments to the system’s realtime clock. Finally, note that the code in this example won’t yield accurate timings unless action does a fair amount of work, say a second or more. Accurate microbenchmarking is notoriously hard and is best done with the aid of a specialized framework such as jmh [JMH].
還有一些細節值得注意。傳遞給 time 方法的 executor 必須允許創建至少與給定并發級別相同數量的線程,否則測試將永遠不會完成。這被稱為線程饑餓死鎖 [Goetz06, 8.1.1]。如果工作線程捕捉到 InterruptedException,它使用習慣用法 `Thread.currentThread().interrupt()` 重申中斷,并從它的 run 方法返回。這允許執行程序按照它認為合適的方式處理中斷。請注意,`System.nanoTime` 是用來計時的。**對于間隔計時,始終使用 `System.nanoTime` 而不是 `System.currentTimeMillis`。** `System.nanoTime` 不僅更準確,而且更精確,而且不受系統實時時鐘調整的影響。最后,請注意,本例中的代碼不會產生準確的計時,除非 action 做了相當多的工作,比如一秒鐘或更長時間。準確的微基準測試是出了名的困難,最好是借助諸如 jmh 這樣的專業框架來完成。
This item only scratches the surface of what you can do with the concurrency utilities. For example, the three countdown latches in the previous example could be replaced by a single CyclicBarrier or Phaser instance. The resulting code would be a bit more concise but perhaps more difficult to understand.
本條目只涉及到你可以使用并發實用工具做什么。例如,前面示例中的三個倒計時鎖存器可以替換為單個 CyclicBarrier 或 Phaser 實例。生成的代碼可能更簡潔,但可能更難于理解。
While you should always use the concurrency utilities in preference to wait and notify, you might have to maintain legacy code that uses wait and notify. The wait method is used to make a thread wait for some condition. It must be invoked inside a synchronized region that locks the object on which it is invoked. Here is the standard idiom for using the wait method:
雖然你應該始終優先使用并發實用工具,而不是使用 wait 和 notify,但是你可能必須維護使用 wait 和 notify 的遺留代碼。wait 方法用于使線程等待某個條件。它必須在同步區域內調用,該同步區域將鎖定調用它的對象。下面是使用 wait 方法的標準形式:
```
// The standard idiom for using the wait method
synchronized (obj) {
while (<condition does not hold>)
obj.wait(); // (Releases lock, and reacquires on wakeup)
... // Perform action appropriate to condition
}
```
**Always use the wait loop idiom to invoke the wait method; never invoke it outside of a loop.** The loop serves to test the condition before and after waiting.
**始終使用 wait 習慣用法,即循環來調用 wait 方法;永遠不要在循環之外調用它。** 循環用于在等待之前和之后測試條件。
Testing the condition before waiting and skipping the wait if the condition already holds are necessary to ensure liveness. If the condition already holds and the notify (or notifyAll) method has already been invoked before a thread waits, there is no guarantee that the thread will ever wake from the wait.
在等待之前測試條件,如果條件已經存在,則跳過等待,以確保活性。如果條件已經存在,并且在線程等待之前已經調用了 notify(或 notifyAll)方法,則不能保證線程將從等待中喚醒。
Testing the condition after waiting and waiting again if the condition does not hold are necessary to ensure safety. If the thread proceeds with the action when the condition does not hold, it can destroy the invariant guarded by the lock. There are several reasons a thread might wake up when the condition does not hold:
為了確保安全,需要在等待之后再測試條件,如果條件不成立,則再次等待。如果線程在條件不成立的情況下繼續執行該操作,它可能會破壞由鎖保護的不變性。當條件不成立時,有一些理由喚醒線程:
- Another thread could have obtained the lock and changed the guarded state between the time a thread invoked notify and the waiting thread woke up.
另一個線程可以獲得鎖,并在線程調用 notify 和等待線程醒來之間更改保護狀態。
- Another thread could have invoked notify accidentally or maliciously when the condition did not hold. Classes expose themselves to this sort of mischief by waiting on publicly accessible objects. Any wait in a synchronized method of a publicly accessible object is susceptible to this problem.
當條件不成立時,另一個線程可能意外地或惡意地調用 notify。類通過等待公共可訪問的對象來暴露自己。公共可訪問對象的同步方法中的任何 wait 都容易受到這個問題的影響。
- The notifying thread could be overly “generous” in waking waiting threads. For example, the notifying thread might invoke notifyAll even if only some of the waiting threads have their condition satisfied.
通知線程在喚醒等待線程時可能過于「慷慨」。例如,即使只有一些等待線程的條件得到滿足,通知線程也可能調用 notifyAll。
- The waiting thread could (rarely) wake up in the absence of a notify. This is known as a spurious wakeup [POSIX, 11.4.3.6.1; Java9-api].
在沒有通知的情況下,等待的線程可能(很少)醒來。這被稱為偽喚醒 [POSIX, 11.4.3.6.1; Java9-api]。
A related issue is whether to use notify or notifyAll to wake waiting threads. (Recall that notify wakes a single waiting thread, assuming such a thread exists, and notifyAll wakes all waiting threads.) It is sometimes said that you should always use notifyAll. This is reasonable, conservative advice. It will always yield correct results because it guarantees that you’ll wake the threads that need to be awakened. You may wake some other threads, too, but this won’t affect the correctness of your program. These threads will check the condition for which they’re waiting and, finding it false, will continue waiting.
一個相關的問題是,是使用 notify 還是 notifyAll 來喚醒等待的線程。(回想一下 notify 喚醒一個等待線程,假設存在這樣一個線程,notifyAll 喚醒所有等待線程)。有時人們會說,應該始終使用 notifyAll。這是合理的、保守的建議。它總是會產生正確的結果,因為它保證你將喚醒需要喚醒的線程。你可能還會喚醒其他一些線程,但這不會影響程序的正確性。這些線程將檢查它們正在等待的條件,如果發現為條件不滿足,將繼續等待。
As an optimization, you may choose to invoke notify instead of notifyAll if all threads that could be in the wait-set are waiting for the same condition and only one thread at a time can benefit from the condition becoming true.
作為一種優化,如果在等待狀態的所有線程都在等待相同的條件,并且每次只有一個線程可以從條件中獲益,那么你可以選擇調用 notify 而不是 notifyAll。
Even if these preconditions are satisfied, there may be cause to use notifyAll in place of notify. Just as placing the wait invocation in a loop protects against accidental or malicious notifications on a publicly accessible object, using notifyAll in place of notify protects against accidental or malicious waits by an unrelated thread. Such waits could otherwise “swallow” a critical notification, leaving its intended recipient waiting indefinitely.
即使滿足了這些先決條件,也可能有理由使用 notifyAll 來代替 notify。正如將 wait 調用放在循環中可以防止公共訪問對象上的意外或惡意通知一樣,使用 notifyAll 代替 notify 可以防止不相關線程的意外或惡意等待。否則,這樣的等待可能會「吞下」一個關鍵通知,讓預期的接收者無限期地等待。
In summary, using wait and notify directly is like programming in “concurrency assembly language,” as compared to the higher-level language provided by java.util.concurrent. **There is seldom, if ever, a reason to use wait and notify in new code.** If you maintain code that uses wait and notify, make sure that it always invokes wait from within a while loop using the standard idiom. The notifyAll method should generally be used in preference to notify. If notify is used, great care must be taken to ensure liveness.
總之,與 `java.util.concurrent` 提供的高級語言相比,直接使用 wait 和 notify 就像使用「并發匯編語言」編程一樣原始。**在新代碼中很少有理由使用 wait 和 notify。** 如果維護使用 wait 和 notify 的代碼,請確保它始終使用標準的習慣用法,即在 while 循環中調用 wait。另外,notifyAll 方法通常應該優先于 notify。如果使用 notify,則必須非常小心以確保其活性。
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-11/Chapter-11-Introduction.md)**
- **Previous Item(上一條目):[Item 80: Prefer executors, tasks, and streams to threads(Executor、task、流優于直接使用線程)](/Chapter-11/Chapter-11-Item-80-Prefer-executors,-tasks,-and-streams-to-threads.md)**
- **Next Item(下一條目):[Item 82: Document thread safety(文檔應包含線程安全屬性)](/Chapter-11/Chapter-11-Item-82-Document-thread-safety.md)**
- Chapter 2. Creating and Destroying Objects(創建和銷毀對象)
- Item 1: Consider static factory methods instead of constructors(考慮以靜態工廠方法代替構造函數)
- Item 2: Consider a builder when faced with many constructor parameters(在面對多個構造函數參數時,請考慮構建器)
- Item 3: Enforce the singleton property with a private constructor or an enum type(使用私有構造函數或枚舉類型實施單例屬性)
- Item 4: Enforce noninstantiability with a private constructor(用私有構造函數實施不可實例化)
- Item 5: Prefer dependency injection to hardwiring resources(依賴注入優于硬連接資源)
- Item 6: Avoid creating unnecessary objects(避免創建不必要的對象)
- Item 7: Eliminate obsolete object references(排除過時的對象引用)
- Item 8: Avoid finalizers and cleaners(避免使用終結器和清除器)
- Item 9: Prefer try with resources to try finally(使用 try-with-resources 優于 try-finally)
- Chapter 3. Methods Common to All Objects(對象的通用方法)
- Item 10: Obey the general contract when overriding equals(覆蓋 equals 方法時應遵守的約定)
- Item 11: Always override hashCode when you override equals(當覆蓋 equals 方法時,總要覆蓋 hashCode 方法)
- Item 12: Always override toString(始終覆蓋 toString 方法)
- Item 13: Override clone judiciously(明智地覆蓋 clone 方法)
- Item 14: Consider implementing Comparable(考慮實現 Comparable 接口)
- Chapter 4. Classes and Interfaces(類和接口)
- Item 15: Minimize the accessibility of classes and members(盡量減少類和成員的可訪問性)
- Item 16: In public classes use accessor methods not public fields(在公共類中,使用訪問器方法,而不是公共字段)
- Item 17: Minimize mutability(減少可變性)
- Item 18: Favor composition over inheritance(優先選擇復合而不是繼承)
- Item 19: Design and document for inheritance or else prohibit it(繼承要設計良好并且具有文檔,否則禁止使用)
- Item 20: Prefer interfaces to abstract classes(接口優于抽象類)
- Item 21: Design interfaces for posterity(為后代設計接口)
- Item 22: Use interfaces only to define types(接口只用于定義類型)
- Item 23: Prefer class hierarchies to tagged classes(類層次結構優于帶標簽的類)
- Item 24: Favor static member classes over nonstatic(靜態成員類優于非靜態成員類)
- Item 25: Limit source files to a single top level class(源文件僅限有單個頂層類)
- Chapter 5. Generics(泛型)
- Item 26: Do not use raw types(不要使用原始類型)
- Item 27: Eliminate unchecked warnings(消除 unchecked 警告)
- Item 28: Prefer lists to arrays(list 優于數組)
- Item 29: Favor generic types(優先使用泛型)
- Item 30: Favor generic methods(優先使用泛型方法)
- Item 31: Use bounded wildcards to increase API flexibility(使用有界通配符增加 API 的靈活性)
- Item 32: Combine generics and varargs judiciously(明智地合用泛型和可變參數)
- Item 33: Consider typesafe heterogeneous containers(考慮類型安全的異構容器)
- Chapter 6. Enums and Annotations(枚舉和注解)
- Item 34: Use enums instead of int constants(用枚舉類型代替 int 常量)
- Item 35: Use instance fields instead of ordinals(使用實例字段替代序數)
- Item 36: Use EnumSet instead of bit fields(用 EnumSet 替代位字段)
- Item 37: Use EnumMap instead of ordinal indexing(使用 EnumMap 替換序數索引)
- Item 38: Emulate extensible enums with interfaces(使用接口模擬可擴展枚舉)
- Item 39: Prefer annotations to naming patterns(注解優于命名模式)
- Item 40: Consistently use the Override annotation(堅持使用 @Override 注解)
- Item 41: Use marker interfaces to define types(使用標記接口定義類型)
- Chapter 7. Lambdas and Streams(λ 表達式和流)
- Item 42: Prefer lambdas to anonymous classes(λ 表達式優于匿名類)
- Item 43: Prefer method references to lambdas(方法引用優于 λ 表達式)
- Item 44: Favor the use of standard functional interfaces(優先使用標準函數式接口)
- Item 45: Use streams judiciously(明智地使用流)
- Item 46: Prefer side effect free functions in streams(在流中使用無副作用的函數)
- Item 47: Prefer Collection to Stream as a return type(優先選擇 Collection 而不是流作為返回類型)
- Item 48: Use caution when making streams parallel(謹慎使用并行流)
- Chapter 8. Methods(方法)
- Item 49: Check parameters for validity(檢查參數的有效性)
- Item 50: Make defensive copies when needed(在需要時制作防御性副本)
- Item 51: Design method signatures carefully(仔細設計方法簽名)
- Item 52: Use overloading judiciously(明智地使用重載)
- Item 53: Use varargs judiciously(明智地使用可變參數)
- Item 54: Return empty collections or arrays, not nulls(返回空集合或數組,而不是 null)
- Item 55: Return optionals judiciously(明智地的返回 Optional)
- Item 56: Write doc comments for all exposed API elements(為所有公開的 API 元素編寫文檔注釋)
- Chapter 9. General Programming(通用程序設計)
- Item 57: Minimize the scope of local variables(將局部變量的作用域最小化)
- Item 58: Prefer for-each loops to traditional for loops(for-each 循環優于傳統的 for 循環)
- Item 59: Know and use the libraries(了解并使用庫)
- Item 60: Avoid float and double if exact answers are required(若需要精確答案就應避免使用 float 和 double 類型)
- Item 61: Prefer primitive types to boxed primitives(基本數據類型優于包裝類)
- Item 62: Avoid strings where other types are more appropriate(其他類型更合適時應避免使用字符串)
- Item 63: Beware the performance of string concatenation(當心字符串連接引起的性能問題)
- Item 64: Refer to objects by their interfaces(通過接口引用對象)
- Item 65: Prefer interfaces to reflection(接口優于反射)
- Item 66: Use native methods judiciously(明智地使用本地方法)
- Item 67: Optimize judiciously(明智地進行優化)
- Item 68: Adhere to generally accepted naming conventions(遵守被廣泛認可的命名約定)
- Chapter 10. Exceptions(異常)
- Item 69: Use exceptions only for exceptional conditions(僅在確有異常條件下使用異常)
- Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors(對可恢復情況使用 checked 異常,對編程錯誤使用運行時異常)
- Item 71: Avoid unnecessary use of checked exceptions(避免不必要地使用 checked 異常)
- Item 72: Favor the use of standard exceptions(鼓勵復用標準異常)
- Item 73: Throw exceptions appropriate to the abstraction(拋出能用抽象解釋的異常)
- Item 74: Document all exceptions thrown by each method(為每個方法記錄會拋出的所有異常)
- Item 75: Include failure capture information in detail messages(異常詳細消息中應包含捕獲失敗的信息)
- Item 76: Strive for failure atomicity(盡力保證故障原子性)
- Item 77: Don’t ignore exceptions(不要忽略異常)
- Chapter 11. Concurrency(并發)
- Item 78: Synchronize access to shared mutable data(對共享可變數據的同步訪問)
- Item 79: Avoid excessive synchronization(避免過度同步)
- Item 80: Prefer executors, tasks, and streams to threads(Executor、task、流優于直接使用線程)
- Item 81: Prefer concurrency utilities to wait and notify(并發實用工具優于 wait 和 notify)
- Item 82: Document thread safety(文檔應包含線程安全屬性)
- Item 83: Use lazy initialization judiciously(明智地使用延遲初始化)
- Item 84: Don’t depend on the thread scheduler(不要依賴線程調度器)
- Chapter 12. Serialization(序列化)
- Item 85: Prefer alternatives to Java serialization(優先選擇 Java 序列化的替代方案)
- Item 86: Implement Serializable with great caution(非常謹慎地實現 Serializable)
- Item 87: Consider using a custom serialized form(考慮使用自定義序列化形式)
- Item 88: Write readObject methods defensively(防御性地編寫 readObject 方法)
- Item 89: For instance control, prefer enum types to readResolve(對于實例控制,枚舉類型優于 readResolve)
- Item 90: Consider serialization proxies instead of serialized instances(考慮以序列化代理代替序列化實例)