## Chapter 11. Concurrency(并發)
### Item 79: Avoid excessive synchronization(避免過度同步)
Item 78 warns of the dangers of insufficient synchronization. This item concerns the opposite problem. Depending on the situation, excessive synchronization can cause reduced performance, deadlock, or even nondeterministic behavior.
[Item-78](/Chapter-11/Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md) 警告我們同步不到位的危險。本條目涉及相反的問題。根據不同的情況,過度的同步可能導致性能下降、死鎖甚至不確定行為。
**To avoid liveness and safety failures, never cede control to the client within a synchronized method or block.** In other words, inside a synchronized region, do not invoke a method that is designed to be overridden, or one provided by a client in the form of a function object (Item 24). From the perspective of the class with the synchronized region, such methods are alien. The class has no knowledge of what the method does and has no control over it. Depending on what an alien method does, calling it from a synchronized region can cause exceptions, deadlocks, or data corruption.
**為避免活性失敗和安全故障,永遠不要在同步方法或塊中將控制權交給客戶端。** 換句話說,在同步區域內,不要調用一個設計為被覆蓋的方法,或者一個由客戶端以函數對象的形式提供的方法([Item-24](/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。從具有同步區域的類的角度來看,這種方法是不一樣的。類不知道該方法做什么,也無法控制它。Depending on what an alien method does,從同步區域調用它可能會導致異常、死鎖或數據損壞。
To make this concrete, consider the following class, which implements an observable set wrapper. It allows clients to subscribe to notifications when elements are added to the set. This is the Observer pattern [Gamma95]. For brevity’s sake, the class does not provide notifications when elements are removed from the set, but it would be a simple matter to provide them. This class is implemented atop the reusable ForwardingSet from Item 18 (page 90):
要使這個問題具體化,請考慮下面的類,它實現了一個可視 Set 包裝器。當元素被添加到集合中時,它允許客戶端訂閱通知。這是觀察者模式 [Gamma95]。為了簡單起見,當元素從集合中刪除時,該類不提供通知,即使要提供通知也很簡單。這個類是在 [Item-18](/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md)(第 90 頁)的可復用 ForwardingSet 上實現的:
```
// Broken - invokes alien method from synchronized block!
public class ObservableSet<E> extends ForwardingSet<E> {
public ObservableSet(Set<E> set) { super(set); }
private final List<SetObserver<E>> observers= new ArrayList<>();
public void addObserver(SetObserver<E> observer) {
synchronized(observers) {
observers.add(observer);
}
}
public boolean removeObserver(SetObserver<E> observer) {
synchronized(observers) {
return observers.remove(observer);
}
}
private void notifyElementAdded(E element) {
synchronized(observers) {
for (SetObserver<E> observer : observers)
observer.added(this, element);
}
}
@Override
public boolean add(E element) {
boolean added = super.add(element);
if (added)
notifyElementAdded(element);
return added;
}
@Override
public boolean addAll(Collection<? extends E> c) {
boolean result = false;
for (E element : c)
result |= add(element); // Calls notifyElementAdded
return result;
}
}
```
Observers subscribe to notifications by invoking the addObserver method and unsubscribe by invoking the removeObserver method. In both cases, an instance of this callback interface is passed to the method.
觀察者通過調用 addObserver 方法訂閱通知,通過調用 removeObserver 方法取消訂閱。在這兩種情況下,都會將此回調接口的實例傳遞給方法。
```
@FunctionalInterface
public interface SetObserver<E> {
// Invoked when an element is added to the observable set
void added(ObservableSet<E> set, E element);
}
```
This interface is structurally identical to `BiConsumer<ObservableSet<E>,E>`. We chose to define a custom functional interface because the interface and method names make the code more readable and because the interface could evolve to incorporate multiple callbacks. That said, a reasonable argument could also be made for using BiConsumer (Item 44).
這個接口在結構上與 `BiConsumer<ObservableSet<E>,E>` 相同。我們選擇定義一個自定義函數式接口,因為接口和方法名稱使代碼更具可讀性,而且接口可以演化為包含多個回調。也就是說,使用 BiConsumer 也是合理的([Item-44](/Chapter-7/Chapter-7-Item-44-Favor-the-use-of-standard-functional-interfaces.md))。
On cursory inspection, ObservableSet appears to work fine. For example, the following program prints the numbers from 0 through 99:
粗略地檢查一下,ObservableSet 似乎工作得很好。例如,下面的程序打印從 0 到 99 的數字:
```
public static void main(String[] args) {
ObservableSet<Integer> set =new ObservableSet<>(new HashSet<>());
set.addObserver((s, e) -> System.out.println(e));
for (int i = 0; i < 100; i++)
set.add(i);
}
```
Now let’s try something a bit fancier. Suppose we replace the addObserver call with one that passes an observer that prints the Integer value that was added to the set and removes itself if the value is 23:
現在讓我們嘗試一些更奇特的東西。假設我們將 addObserver 調用替換為一個傳遞觀察者的調用,該觀察者打印添加到集合中的整數值,如果該值為 23,則該調用將刪除自身:
```
set.addObserver(new SetObserver<>() {
public void added(ObservableSet<Integer> s, Integer e) {
System.out.println(e);
if (e == 23)
s.removeObserver(this);
}
});
```
Note that this call uses an anonymous class instance in place of the lambda used in the previous call. That is because the function object needs to pass itself to s.removeObserver, and lambdas cannot access themselves (Item 42).
注意,這個調用使用一個匿名類實例來代替前面調用中使用的 lambda 表達式。這是因為函數對象需要將自己傳遞給 `s.removeObserver`,而 lambda 表達式不能訪問自身([Item-42](/Chapter-7/Chapter-7-Item-42-Prefer-lambdas-to-anonymous-classes.md))。
You might expect the program to print the numbers 0 through 23, after which the observer would unsubscribe and the program would terminate silently. In fact, it prints these numbers and then throws a ConcurrentModificationException. The problem is that notifyElementAdded is in the process of iterating over the observers list when it invokes the observer’s added method. The added method calls the observable set’s removeObserver method, which in turn calls the method observers.remove. Now we’re in trouble. We are trying to remove an element from a list in the midst of iterating over it, which is illegal. The iteration in the notifyElementAdded method is in a synchronized block to prevent concurrent modification, but it doesn’t prevent the iterating thread itself from calling back into the observable set and modifying its observers list.
你可能希望程序打印數字 0 到 23,然后觀察者將取消訂閱,程序將無聲地終止。實際上,它打印這些數字,然后拋出 ConcurrentModificationException。問題在于 notifyElementAdded 在調用觀察者的 added 方法時,正在遍歷 observers 列表。added 方法調用可觀察集的 removeObserver 方法,該方法反過來調用方法 `observers.remove`。現在我們有麻煩了。我們試圖在遍歷列表的過程中從列表中刪除一個元素,這是非法的。notifyElementAdded 方法中的迭代位于一個同步塊中,以防止并發修改,但是無法防止迭代線程本身回調到可觀察的集合中,也無法防止修改它的 observers 列表。
Now let’s try something odd: let’s write an observer that tries to unsubscribe, but instead of calling removeObserver directly, it engages the services of another thread to do the deed. This observer uses an executor service (Item 80):
現在讓我們嘗試一些奇怪的事情:讓我們編寫一個觀察者來嘗試取消訂閱,但是它沒有直接調用 removeObserver,而是使用另一個線程的服務來執行這個操作。該觀察者使用 executor 服務([Item-80](/Chapter-11/Chapter-11-Item-80-Prefer-executors,-tasks,-and-streams-to-threads.md)):
```
// Observer that uses a background thread needlessly
set.addObserver(new SetObserver<>() {
public void added(ObservableSet<Integer> s, Integer e) {
System.out.println(e);
if (e == 23) {
ExecutorService exec = Executors.newSingleThreadExecutor();
try {
exec.submit(() -> s.removeObserver(this)).get();
} catch (ExecutionException | InterruptedException ex) {
throw new AssertionError(ex);
} finally {
exec.shutdown();
}
}
}
});
```
Incidentally, note that this program catches two different exception types in one catch clause. This facility, informally known as multi-catch, was added in Java 7. It can greatly increase the clarity and reduce the size of programs that behave the same way in response to multiple exception types.
順便提一下,注意這個程序在一個 catch 子句中捕獲了兩種不同的異常類型。這個功能在 Java 7 中添加了,非正式名稱為 multi-catch。它可以極大地提高清晰度,并減少在響應多種異常類型時表現相同的程序的大小。
When we run this program, we don’t get an exception; we get a deadlock. The background thread calls s.removeObserver, which attempts to lock observers, but it can’t acquire the lock, because the main thread already has the lock. All the while, the main thread is waiting for the background thread to finish removing the observer, which explains the deadlock.
當我們運行這個程序時,我們不會得到異常;而是遭遇了死鎖。后臺線程調用 `s.removeObserver`,它試圖鎖定觀察者,但無法獲取鎖,因為主線程已經擁有鎖。一直以來,主線程都在等待后臺線程完成刪除觀察者的操作,這就解釋了死鎖的原因。
This example is contrived because there is no reason for the observer to use a background thread to unsubscribe itself, but the problem is real. Invoking alien methods from within synchronized regions has caused many deadlocks in real systems, such as GUI toolkits.
這個例子是人為設計的,因為觀察者沒有理由使用后臺線程來取消訂閱本身,但是問題是真實的。在實際系統中,從同步區域內調用外來方法會導致許多死鎖,比如 GUI 工具包。
In both of the previous examples (the exception and the deadlock) we were lucky. The resource that was guarded by the synchronized region (observers) was in a consistent state when the alien method (added) was invoked. Suppose you were to invoke an alien method from a synchronized region while the invariant protected by the synchronized region was temporarily invalid. Because locks in the Java programming language are reentrant, such calls won’t deadlock. As in the first example, which resulted in an exception, the calling thread already holds the lock, so the thread will succeed when it tries to reacquire the lock, even though another conceptually unrelated operation is in progress on the data guarded by the lock. The consequences of such a failure can be catastrophic. In essence, the lock has failed to do its job. Reentrant locks simplify the construction of multithreaded object-oriented programs, but they can turn liveness failures into safety failures.
在前面的兩個例子中(異常和死鎖),我們都很幸運。調用外來方法(added)時,由同步區域(觀察者)保護的資源處于一致狀態。假設你要從同步區域調用一個外來方法,而同步區域保護的不變量暫時無效。因為 Java 編程語言中的鎖是可重入的,所以這樣的調用不會死鎖。與第一個導致異常的示例一樣,調用線程已經持有鎖,所以當它試圖重新獲得鎖時,線程將成功,即使另一個概念上不相關的操作正在對鎖保護的數據進行中。這種失敗的后果可能是災難性的。從本質上說,這把鎖沒能發揮它的作用。可重入鎖簡化了多線程面向對象程序的構造,但它們可以將活動故障轉化為安全故障。
Luckily, it is usually not too hard to fix this sort of problem by moving alien method invocations out of synchronized blocks. For the notifyElementAdded method, this involves taking a “snapshot” of the observers list that can then be safely traversed without a lock. With this change, both of the previous examples run without exception or deadlock:
幸運的是,通過將外來方法調用移出同步塊來解決這類問題通常并不難。對于 notifyElementAdded 方法,這涉及到獲取觀察者列表的「快照」,然后可以在沒有鎖的情況下安全地遍歷該列表。有了這個改變,前面的兩個例子都可以再也不會出現異常或者死鎖了:
```
// Alien method moved outside of synchronized block - open calls
private void notifyElementAdded(E element) {
List<SetObserver<E>> snapshot = null;
synchronized(observers) {
snapshot = new ArrayList<>(observers);
}
for (SetObserver<E> observer :snapshot)
observer.added(this, element);
}
```
In fact, there’s a better way to move the alien method invocations out of the synchronized block. The libraries provide a concurrent collection (Item 81) known as CopyOnWriteArrayList that is tailor-made for this purpose. This List implementation is a variant of ArrayList in which all modification operations are implemented by making a fresh copy of the entire underlying array. Because the internal array is never modified, iteration requires no locking and is very fast. For most uses, the performance of CopyOnWriteArrayList would be atrocious, but it’s perfect for observer lists, which are rarely modified and often traversed.
實際上,有一種更好的方法可以將外來方法調用移出同步塊。庫提供了一個名為 CopyOnWriteArrayList 的并發集合([Item-81](/Chapter-11/Chapter-11-Item-81-Prefer-concurrency-utilities-to-wait-and-notify.md)),該集合是為此目的量身定制的。此列表實現是 ArrayList 的變體,其中所有修改操作都是通過復制整個底層數組來實現的。因為從不修改內部數組,所以迭代不需要鎖定,而且速度非常快。如果大量使用,CopyOnWriteArrayList 的性能會很差,但是對于很少修改和經常遍歷的觀察者列表來說,它是完美的。
The add and addAll methods of ObservableSet need not be changed if the list is modified to use CopyOnWriteArrayList. Here is how the remainder of the class looks. Notice that there is no explicit synchronization whatsoever:
如果將 list 修改為使用 CopyOnWriteArrayList,則不需要更改 ObservableSet 的 add 和 addAll 方法。下面是類的其余部分。請注意,沒有任何顯式同步:
```
// Thread-safe observable set with CopyOnWriteArrayList
private final List<SetObserver<E>> observers =new CopyOnWriteArrayList<>();
public void addObserver(SetObserver<E> observer) {
observers.add(observer);
}
public boolean removeObserver(SetObserver<E> observer) {
return observers.remove(observer);
}
private void notifyElementAdded(E element) {
for (SetObserver<E> observer : observers)
observer.added(this, element);
}
```
An alien method invoked outside of a synchronized region is known as an open call [Goetz06, 10.1.4]. Besides preventing failures, open calls can greatly increase concurrency. An alien method might run for an arbitrarily long period. If the alien method were invoked from a synchronized region, other threads would be denied access to the protected resource unnecessarily.
在同步區域之外調用的外來方法稱為 open call [Goetz06, 10.1.4]。除了防止失敗之外,開放調用還可以極大地提高并發性。一個陌生的方法可以運行任意長的時間。如果從同步區域調用了外來方法,其他線程對受保護資源的訪問就會遭到不必要的拒絕。
**As a rule, you should do as little work as possible inside synchronized regions.** Obtain the lock, examine the shared data, transform it as necessary, and drop the lock. If you must perform some time-consuming activity, find a way to move it out of the synchronized region without violating the guidelines in Item 78.
**作為規則,你應該在同步區域內做盡可能少的工作。** 獲取鎖,檢查共享數據,根據需要進行轉換,然后刪除鎖。如果你必須執行一些耗時的活動,請設法將其移出同步區域,而不違反 [Item-78](/Chapter-11/Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md) 中的指導原則。
The first part of this item was about correctness. Now let’s take a brief look at performance. While the cost of synchronization has plummeted since the early days of Java, it is more important than ever not to oversynchronize. In a multicore world, the real cost of excessive synchronization is not the CPU time spent getting locks; it is contention: the lost opportunities for parallelism and the delays imposed by the need to ensure that every core has a consistent view of memory. Another hidden cost of oversynchronization is that it can limit the VM’s ability to optimize code execution.
本條目的第一部分是關于正確性的。現在讓我們簡要地看一下性能。雖然自 Java 早期以來,同步的成本已經大幅下降,但比以往任何時候都更重要的是:不要過度同步。在多核世界中,過度同步的真正代價不是獲得鎖所花費的 CPU 時間;這是一種爭論:而是失去了并行化的機會,以及由于需要確保每個核心都有一個一致的內存視圖而造成的延遲。過度同步的另一個隱藏成本是,它可能限制 VM 優化代碼執行的能力。
If you are writing a mutable class, you have two options: you can omit all synchronization and allow the client to synchronize externally if concurrent use is desired, or you can synchronize internally, making the class thread-safe (Item 82). You should choose the latter option only if you can achieve significantly higher concurrency with internal synchronization than you could by having the client lock the entire object externally. The collections in java.util (with the exception of the obsolete Vector and Hashtable) take the former approach, while those in java.util.concurrent take the latter (Item 81).
如果你正在編寫一個可變的類,你有兩個選擇:你可以省略所有同步并允許客戶端在需要并發使用時在外部進行同步,或者你可以在內部進行同步,從而使類是線程安全的([Item-82](/Chapter-11/Chapter-11-Item-82-Document-thread-safety.md))。只有當你能夠通過內部同步實現比通過讓客戶端在外部鎖定整個對象獲得高得多的并發性時,才應該選擇后者。`java.util` 中的集合(廢棄的 Vector 和 Hashtable 除外)采用前一種方法,而 `java.util.concurrent` 中的方法則采用后者([Item-81](/Chapter-11/Chapter-11-Item-81-Prefer-concurrency-utilities-to-wait-and-notify.md))。
In the early days of Java, many classes violated these guidelines. For example, StringBuffer instances are almost always used by a single thread, yet they perform internal synchronization. It is for this reason that StringBuffer was supplanted by StringBuilder, which is just an unsynchronized StringBuffer. Similarly, it’s a large part of the reason that the thread-safe pseudorandom number generator in java.util.Random was supplanted by the unsynchronized implementation in java.util.concurrent.ThreadLocalRandom. When in doubt, do not synchronize your class, but document that it is not thread-safe.
在 Java 的早期,許多類違反了這些準則。例如,StringBuffer 實例幾乎總是由一個線程使用,但是它們執行內部同步。正是由于這個原因,StringBuffer 被 StringBuilder 取代,而 StringBuilder 只是一個未同步的 StringBuffer。類似地,同樣,`java.util.Random` 中的線程安全偽隨機數生成器被 `java.util.concurrent.ThreadLocalRandom` 中的非同步實現所取代,這也是原因之一。如果有疑問,不要同步你的類,但要記錄它不是線程安全的。
If you do synchronize your class internally, you can use various techniques to achieve high concurrency, such as lock splitting, lock striping, and nonblocking concurrency control. These techniques are beyond the scope of this book, but they are discussed elsewhere [Goetz06, Herlihy08].
如果你在內部同步你的類,你可以使用各種技術來實現高并發性,例如分拆鎖、分離鎖和非阻塞并發控制。這些技術超出了本書的范圍,但是在其他地方也有討論 [Goetz06, Herlihy08]。
If a method modifies a static field and there is any possibility that the method will be called from multiple threads, you must synchronize access to the field internally (unless the class can tolerate nondeterministic behavior). It is not possible for a multithreaded client to perform external synchronization on such a method, because unrelated clients can invoke the method without synchronization. The field is essentially a global variable even if it is private because it can be read and modified by unrelated clients. The nextSerialNumber field used by the method generateSerialNumber in Item 78 exemplifies this situation.
如果一個方法修改了一個靜態字段,并且有可能從多個線程調用該方法,則必須在內部同步對該字段的訪問(除非該類能夠容忍不確定性行為)。多線程客戶端不可能對這樣的方法執行外部同步,因為不相關的客戶端可以在不同步的情況下調用該方法。字段本質上是一個全局變量,即使它是私有的,因為它可以被不相關的客戶端讀取和修改。[Item-78](/Chapter-11/Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md) 中的 generateSerialNumber 方法使用的 nextSerialNumber 字段演示了這種情況。
In summary, to avoid deadlock and data corruption, never call an alien method from within a synchronized region. More generally, keep the amount of work that you do from within synchronized regions to a minimum. When you are designing a mutable class, think about whether it should do its own synchronization. In the multicore era, it is more important than ever not to oversynchronize. Synchronize your class internally only if there is a good reason to do so, and document your decision clearly (Item 82).
總之,為了避免死鎖和數據損壞,永遠不要從同步區域內調用外來方法。更一般地說,將你在同步區域內所做的工作量保持在最小。在設計可變類時,請考慮它是否應該執行自己的同步。在多核時代,比以往任何時候都更重要的是不要過度同步。只有在有充分理由時,才在內部同步類,并清楚地記錄你的決定([Item-82](/Chapter-11/Chapter-11-Item-82-Document-thread-safety.md))。
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-11/Chapter-11-Introduction.md)**
- **Previous Item(上一條目):[Item 78: Synchronize access to shared mutable data(對共享可變數據的同步訪問)](/Chapter-11/Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md)**
- **Next 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)**
- 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(考慮以序列化代理代替序列化實例)