<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 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)**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看