zoukankan      html  css  js  c++  java
  • Effective Java 69 Prefer concurrency utilities to wait and notify

    Principle

    Use the higher-level concurrency utilities instead of wait and notify for easiness.

    Use ConcurrentHashMap in preference to Collections.synchronizedMap or Hashtable.

    Use concurrent collections in preference to externally synchronized collections.

       

    Three categories of higher-level utilities in java.util.concurrent

    1. Executor Framework (Item 68)
    2. Concurrent collections - provide high- performance concurrent implementations of standard collection interfaces such as List, Queue, and Map.   

      Since all the implementation of Concurrent collections manage their own synchronization internally it's impossible to exclude concurrent activity from a concurrent collection; locking it will have no effect but slow the program.

            // Method simulates the behavior of String.intern. Concurrent canonicalizing map atop ConcurrentMap - faster!

    private static final ConcurrentMap<String, String> map = new ConcurrentHashMap<String, String>();

    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;

    }

    Note

    String.intern must use some sort of weak reference to keep from leaking memory over time.

     

    Blocking operation - wait until they can be successfully performed.

    BlockingQueue (Used for work queues) extends Queue and adds several methods, including take, which removes and returns the head element from the queue, waiting if the queue is empty.

    1. Synchronizers - Objects that enable threads to wait for one another.(eg. CountDownLatch, Semaphore, CyclicBarrier and Exchanger).   

      Countdown latches are single-use barriers that allow one or more threads to wait for one or more other threads to do something.

      /**

      * Concurrency timer demo for "69 Prefer concurrency utilities to wait and notify".

      */

      package com.effectivejava.concurrency;

         

      import java.util.concurrent.CountDownLatch;

      import java.util.concurrent.Executor;

      import java.util.concurrent.SynchronousQueue;

      import java.util.concurrent.ThreadPoolExecutor;

      import java.util.concurrent.TimeUnit;

         

      /**

      * @author Kaibo Hao

      *

      */

      public class ExecutorManager {   

      // Simple framework for timing concurrent execution

      public static long time(Executor executor, int concurrency,

      final Runnable action) throws InterruptedException {

      final CountDownLatch ready = new CountDownLatch(concurrency);

      final CountDownLatch start = new CountDownLatch(1);

      final CountDownLatch done = new CountDownLatch(concurrency);

      for (int i = 0; i < concurrency; i++) {

      executor.execute(new Runnable() {

      public void run() {

      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;

      }

         

      /**

      * @param args

      */

      public static void main(String[] args) {

      try {

      Executor executor = new ThreadPoolExecutor(0, 2, 10,

      TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());

      long executedTime = time(executor, 2, new Runnable() {

      @Override

      public void run() {

      System.out.printf("Runing %s%n", Thread.currentThread());

      }

      });

      System.out.printf("%sns %.3fms %.3fs", executedTime,

      executedTime / 1000.0, executedTime / 1000000.0);

      } catch (InterruptedException e) {

      e.printStackTrace();

      }

      }

      }   

    Note

    If a worker thread catches an InterruptedException, it reasserts the interrupt using the idiom Thread.currentThread().interrupt() and returns from its run method.

    Since System.nanoTime is both more accurate and more precise, and it is not affected by adjustments to the system's real-time clock. For interval timing, always use System.nanoTime in preference to System.currentTimeMillis.   

    Always use the wait loop idiom to invoke the wait method; never invoke it outside of a loop.

    // 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

    }   

    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 time the waiting thread woke.

    • 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 contained in a synchronized method of a publicly accessible object is susceptible to this problem.

    • 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.

    • 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; JavaSE6].

    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.

       

  • 相关阅读:
    感悟优化——Netty对JDK缓冲区的内存池零拷贝改造
    由浅入深理解Java线程池及线程池的如何使用
    Http学习笔记
    zookeeper集群配置详细教程
    kafka学习笔记——基本概念与安装
    干货——详解Java中的关键字
    Java基础巩固——排序
    你可以这么理解五种I/O模型
    Java中的NIO基础知识
    Java基础巩固——异常
  • 原文地址:https://www.cnblogs.com/haokaibo/p/prefer-concurrency-utilities-to-wait-and-notify.html
Copyright © 2011-2022 走看看