"Java Concurrent Programming real" study notes (3)

Chapter Five: Building blocks

Platform library contains a rich set of concurrent building blocks, such as thread-safe container and a variety of synchronization tool ( Synchronizer).

SynchronizerFor regulating the flow of control between the cooperating threads.

Synchronous container

Synchronization containers consists of two parts, one Vectorand HashTableare part of the early JDK; the other is their homologues container was only added in JDK1.2 synchronization package ( wrapper) class.

These classes are a Collections.synchronizedXxxfactory method creates.

The status of these classes by encapsulating their status, and synchronize every public method to achieve the thread-safe, so that only one thread can access the container.

Synchronous container emerging issues

Synchronous container are thread safe. But for complex operations, sometimes you may need to use additional client lock (client-side locking) protection.

Composite container generally comprises: an iterative (elements repeatedly acquired until the last element in the container), navigation (to find the next element in accordance with a certain order) and the operation conditions, such as "lack i.e. added" (put-if- absent), check whether there Map key K, and if not, adding mapping (K, V).

In a synchronous container, these complex operations even if the client does not lock protection, technically thread-safe, but when the other threads to concurrent modification of the container, they might not work as you expect.

/**
 * 操作Vector的复合操作可能导致混乱的结果
 */
public static Object getLast(Vector list) {
  int lastIndex = list.size() - 1;
  return list.get(lastIndex);
}
public static void deleteLast(Vector list) {
  int lastIndex = list.size() - 1;
  list.remove(lastIndex);
}

Here Insert Picture Description

/**
 * 使用客户端加锁,对Vector进行复合操作
 */
public static Object getLast(Vector list) {
  synchronized (list) {
    int lastIndex = list.size() - 1;
    return list.get(lastIndex) ;
  }
}
public static void deleteLast(Vector list) {
  synchronized (list) {
    int lastIndex = list.size() - 1;
    list.remove(lastIndex);
  }
}

Concurrent containers

Java5.0 container through the container to improve the synchronization class provides several concurrent. Concurrent multi-threaded container is designed for concurrent access.

  • Java 5.0 adds ConcurrentHashMapto replace the hash Map to achieve synchronization
  • When the majority operation is a read operation, CopyOnWriteArrayListinstead of the corresponding synchronous implementation List

The new ConcurrentMapinterface is added support for common complex operations, such as "Missing that is added to (put-if-absent)" , replace and delete conditions.

Alternatively synchronous container concurrent containers, there is little risk to this approach brings significant increase scalability.

Java 5.0 is also added two new container types: Queueand BlockingQueue.

  • QueueUsed to temporarily hold a series of elements are awaiting further processing. Based on Queue, a series of concrete implementation.
  • BlockingQueueIt expanded Queue, increasing the blockage can be inserted and acquisition operations. Blocking queue is very useful in the design of producers and consumers.

Just ConcurrentHashMapas the synchronization of concurrent hash Map alternative, Java 6 added ConcurrentSkipListMapand ConcurrentSkipListsetused as synchronous SortedMapand SortedSetconcurrent alternatives (such as a synchronizedMappackage TreeMapor TreeSet).

ConcurrentHashMap

ConcurrentHashMapAnd HashMapas a hash table, but it uses a completely different locking strategy, we can provide better concurrency and scalability.

ConcurrentHashMapUse a more granular locking mechanism, called the separation lock. This mechanism allows a deeper level of shared access. Any number of concurrent read threads can access the Map, readers and writers can also concurrent access Map, and a limited number of write concurrent threads can also modify Map. As a result, lead to higher throughput for concurrent access, while almost no loss of performance of a single thread access.

Blocking queue and producer - consumer model

Blocking queue ( BlockingQueue) provides a blocking putand takemethods, and they can be timed offerand pollare equivalent.

Blocking queue support producer - consumer design pattern. A producer - consumer design isolated "recognition needs to be done" and "implementation." This mode will not find a job will be processed immediately, but to work into a task ( "to do") list, to prepare post-processing.

Producers and consumers to produce different speeds or change the data and consumer, producer - consumer model will decouple these activities, thus simplifying the management workload.

Deque and work stealing

The same Java 6 two new container types, Deque(pronounced Deck) and BlockingDequewhich are respectively expanded Queueand BlockingQueue.

DequeIs a deque , allows efficient insertion and removal of the head and tail, respectively. They realize that ArrayDequeand LinkedBlockingDeque.

As blocking queue applicable to producer - consumer model, like the double-ended queue themselves and called work stealing (work stealing) patterns associated.

A consumer design producer, all consumers share only a work queue; stealing the work of design, each customer has its own double-ended queue. If a consumer to complete all the work himself double-ended queue, it can steal other consumers at the end of double-ended task queue.

Because the worker thread and not compete with a shared task queue, so try to steal than traditional operating mode of producer - consumer design better scalability; most of the time they access their double-ended queue, reducing competition.

When a worker has to access another queue, it will be taken from the tail, rather than from the head, thereby further reducing competition for deque.

Synchronizer

SynchronizerIs an object that controls the flow regulator according to the state of the thread itself.

Blocking queue can play a Synchronizerrole; other types Synchronizer, including semaphore (semaphore), points (barrier) and latch (latch).

Platform libraries exist in some Synchronizerclasses; as if they can not meet your needs, you can also follow as described in Chapter 14, create one of your own Synchronizer.

All Synchronizerare entitled to similar structural characteristics: they encapsulate state and these states to determine the thread execution at a certain point is passed or forced to wait; they also provide state control method, and efficient wait Synchronizermethod into the desired state.

Lockout

Latch (latch) is a Synchronizer, it can delay the progress of the thread until the thread reaches the end (terminal) state.

A locking works like a door: Until lockout reach the end state, the door has been closed, no thread can pass, at the end of the state comes, the door opened, allowing all threads pass.

Atresia can be used to ensure that a particular activity until after the completion of other activities take place, such as:

  • To ensure that a calculation is not executed until the resources it needs to be initialized. Latching a binary (two states) can be used to express "resource has been initialized R", R and all of the necessary activities must wait in the first atresia.
  • To ensure that a service will not start until other services are dependent on it has already begun. Each service will contain an associated binary latch; S will first open the service start to wait for other services in the locking depends s, after the start, the locking is released S, S so that all dependent services can also start the process.
  • Wait until all parts of the activities are fully prepared to continue processing, such as all players in multi-player games whether all ready. Such a lock to be when all players are ready, reach the end state.

CountDownlatchA flexible locking is achieved, for each of these cases; allows a thread to wait or more of a set of events occurs.

Blocking states include a counter, initialized to a positive number, used to show the need to wait for a number of events.

countDown method of operation of the counter decrements to indicate an event has occurred, and await method waits for the counter reaches zero, at which point all events have taken place to wait. If the counter value is non-zero entry, await method will block until the counter is zero, or wait thread is interrupted, and timeout.

FutureTask

FutureTaskIt can also be used as blocking. ( FutureTaskImplementation of abstract describes a portable computing the result).

FutureTaskIt is calculated by the Callableimplementation, which is equivalent to a portable results Runnable, and there are three states: wait, and operation is completed.

Completed including an end to all calculations in any way, including the end of normal, abnormal and canceled. Once FutureTaskentering the completion status, it will never stop in this state.

Future.getThe behavior depends on the status of the task. If it has been completed, getyou can immediately get back the results, otherwise it will be blocked until the task is transferred to the completion status, then returns the result or throw an exception.

FutureTaskThe result of the calculation is transferred from the thread to run the calculation results need this thread: FutureTaskthe Statute to ensure the security release this delivery built on the basis of the results of the above.

/**
 * 使用FutureTask预载稍后需要的数据
 */
public class Preloader {
  
  private final FutureTask<ProductInfo> future = new FutureTask<ProductInfo>(new Callable<ProductInfo>() {
    public ProductInfo call() throws DataLoadException {
      return loadProductInfo();
    }
  });
  
  private final Thread thread = new Thread(future);

  public void start() {
    thread.start();
  }

  public ProductInfo get() throws DataLoadException, InterruptedException {
    try {
      return future.get();
    } catch (ExecutionException e) {
      Throwable cause = e.getCause();
      if (cause instanceof DataLoadException)
        throw (DataLoadException) cause;
      else
        throw launderThrowable(cause);
    }
  }
}

Points

Level ( barrier) similar to the lockout, they were able to block a group of threads, until certain events occur.

The key difference is that the locking points, all threads must arrive at the same checkpoint, in order to continue processing.

Atresia is waiting for an event; checkpoint is waiting for other threads.

Protocol level to achieve, as some family members of the specified collection place in the mall:. "Each of us see at McDonald's at 6:00, to the future not to leave, and then we'll decide what to do next."

CyclicBarrierAllow members of a given number of times in one checkpoint, which is very useful for parallel iterative algorithm, the algorithm will split a problem into a series of independent sub-problems.

When a thread reaches the checkpoint, call await, await will be blocked until all threads have reached the checkpoint. If all the threads have reached the checkpoint, checkpoint was successfully break through, so that all threads have been released, the level is reset to prepare for the next use.

If you call a timeout to await, or blocking the thread is interrupted, then the points will be considered a failure, not all calls to await completion through BrokenBarrierExceptiontermination.

If you successfully pass the level, awaitreturns for each thread reaches a unique index number, you can use it to "election" to produce a leader, the next iteration undertake some special work.

/**
 * 在一个细胞的 自动系统中用CyclicBarrier协调计箅
 */
public class CellularAutomata {
  private final Board mainBoard;
  private final CyclicBarrier barrier;
  private final Worker[] workers;

  public CellularAutomata(Board board) {
    this.mainBoard = board;
    int count = Runtime.getRuntime().availableProcessors();
    this.barrier = new CyclicBarrier(count, () -> mainBoard.commitNewValues());
    this.workers = new Worker[count];
    
    for (int i = 0; i < count; i++) {
      workers[i] = new Worker(mainBoard.getSubBoard(count, i));
    }
  }

  private class Worker implements Runnable {
    private final Board board;

    public Worker(Board board) {
      this.board = board;
    }

    @Override
    public void run() {
      while (!board.hasConverged()) {
        for (int x = 0; x < board.getMaxX(); x++) {
          for (int y = 0; y < board.getMaxY(); y++) {
            board.setNewValue(x, y, computeValue(x, y));
          }
        }
        try {
          barrier.await();
        } catch (InterruptedException ex) {
          return;
        } catch (BrokenBarrierException ex) {
          return;
        }
      }
    }
    public void start() {
      for (int i = 0; i < workers.length; i++) {
        new Thread(workers[i]).start();
      }
      mainBoard.waitForConvergence();
    }
  }
}

Published 107 original articles · won praise 88 · views 260 000 +

Guess you like

Origin blog.csdn.net/Code_shadow/article/details/104499407