Fourth, the basic building block

一、同步容器类

同步容器类包括Vector和Hashtable,Both early part of the JDK, also including functional in JDK 1.2 is in a similar classes, these classes are synchronized by a wrapper Collections.synchronizedXxxlike factory method.这些类实现线程安全的方式是:将它们的状态封装起来,并对每个公有方法都进行同步,使得每次只有一个线程能访问容器的状态。

  • 同步容器类的问题

Synchronous container classes are thread-safe, but in some cases may require additional client lock to protect the complex operation. Common operations composite container comprising: an iterative (repeated access elements until all elements of the container been traversed), jump (to find the next element of the current element based on the specified order), and operation conditions, such as "if not add" (the subject check whether the key K in the presence of the Map, if not, joined tuple (K, V)).在同步容器类中,这些复合操作在没有客户端加锁的情况下仍然是线程安全的,但当其他线程并发地修改容器时,它们可能会表 现出意料之外的行为。

  • 迭代器与 ConcurrentModificationRxception

Errors in the use of iterator (Iterator) This exception is traversing the container will appear.

  • 隐藏迭代器

虽然加锁可以防止迭代器抛出ConcurrentModificationException,但你必须要记住在所有对共享容器进行迭代的地方都需要加锁。实际情况要更加复杂,因为在某些情况下,迭代器会隐藏起来。For example toString () method of the container.

二、并发容器

Java 5.0 provides several containers concurrently to improve performance of the synchronous container.同步容器将所有对容器状态的访问都串行化,以实现它们的线程安全性。这种方法的代价是严重降低并发性,当多个线程竞争 容器的锁时,吞吐量将严重减低。

On the other hand, the container is complicated by concurrent access for multiple threads design. Increase in Concurrent- HashMap in Java 5.0, and used to replace the synchronization based on a hash of the Map, and a CopyOnWriteArrayList, traversal operation for the case where the main synchronization operations in place of List. Added support for some common complex operations in the new ConcurrentMap interfaces, such as "if not then add," and replace conditional deletion.

通过并发容器拉代替同步容器,可以极大第提高伸缩性并降低风险。

Java 5.0 adds two new container types: Queue and BlockingQueue. Queue用来临时保存一组等待处理的元素。它提供了几种实现,包括:ConcurrentLinkedQueue,这是一个传统的先进先出队列,以及PriorityQueue,这是一个(非并发的)优先队列. On-Queue does not block if the queue is empty, then get the elements of the operation will return a null value. Although you can use the Queue List to simulate the behavior - in fact, it is to implement Queue, but also need a Queue class, because it can remove List of random access requirements, in order to achieve more efficient concurrency by LinkedList.

BlockingQueue扩展了 Queue,增加了可阻塞的插入和获取等操作。如果队列为空,那么获取元素的操作将一直阻塞,直到队列中出现一个可用的元素。如果队列已满(对于有界队列来说),那么插入元素的操作将一直阻塞,直到队列中出现可用的空间。在“生产者-消费者” 这种设计模式中,阻塞队列是非常有用的。

As used instead ConcurrentHashMap Map hash-based synchronization, Java 6 and also introduces ConcurrentSkipListMap ConcurrentSkipListSet, respectively as synchronized concurrent SortedSet SortedMap and alternatives (e.g. packed with synchronizedMap TreeMap or TreeSet).

  • ConcurrentHashMap

And HashMap- kind, ConcurrentHashMap is a hash-based Map, but it uses a different locking strategy to provide higher concurrency and scalability. ConcurrentHashMap not every method in synchronization with a lock and such that each time only one thread access to the container, but rather uses a more granular locking mechanism to achieve a greater degree of shared, this mechanism is called a segmented lock (lock Striping). In this mechanism, an arbitrary number of threads can be read concurrently access the Map, threads and thread performs read operation is performed concurrently write operation can access the Map, and a number of threads can concurrently modify write Map.ConcurrentHashMap带来的结果是, 在并发访问环境下将实现更高的吞吐量,而在单线程环境中只损失非常小的性能。

Despite these improvements, there are still some factors to weigh. Some methods need to be calculated over the entire Map, e.g. semantic size and isEmpty, these methods are complicated slightly weakened to reflect characteristics of the container. As a result of size returned in the calculation it may have expired, and it's really just an estimate, thus allowing size to return an approximation rather than a precise value. While this may seem disconcerting, but in fact such methods size and isEmpty useful in concurrent environment is very small, since their return values ​​are always changing. Therefore, the demand for these operations is weakened, in return for performance optimization to other more important operations include get, put, containsKey and remove so on.

ConcurrentHashMap not implemented in the Map locked to provide exclusive access. In Hashtable and synchronized- Map, the lock is acquired Map can prevent other threads access the Map. This is needed, such as adding in some unusual cases by way of a number of mapping atom, or order of elements and keep the same number of times during this iteration Map. However, in general, this trade-off is reasonable, because the contents of the container will be complicated by constant change.

  • 额外的原子Map操作

Since ConcurrentHashMap can not be locked to perform exclusive access, we are unable to create a new atomic operation using a client lock. However, some common combined operation, such as "if not add", "if equal, removing (Remove-If-Equal)" and "equal if the replacement (Replace-If-Equal)" and the like, have been implemented as a atomic operations declared in the interface and the ConcurrentMap. If you need to add this functionality to existing synchronous Map, then it probably means that you should consider using a ConcurrentMap.

  • CopyOnWriteArrayList

List CopyOnWriteArrayList for synchronization Alternatively, in some cases it offers better concurrency, and no container locking or replication during an iteration. (Similarly, the role of alternative CopyOnWriteArraySet synchronous Set.)

三、阻塞队列和生产者-消费者模式

Providing the blocking queue put and take process may obstruction, as well as methods offer support and poll timing. If the queue is full, then put the method will block until space is available; if the queue is empty, then take the method will block until the element is available. Queue can be bounded also be unbounded, unbounded queue will never be filled, so unbounded put method on the queue will never be blocked.

阻塞队列支持生产者-消费者这种设计模式。This mode will "find out the work to be done" and "implementation" of these two processes separated, and the work items into a "to-do" list for the subsequent process, rather than find out immediately after processing. Producer - consumer model to simplify the development process, as it eliminates the class code dependencies between producers and consumers based, in addition, the pattern data will be produced using the process data in a simplified open Decoupling workload management, since the two processes differ in the rate of processing data.

Blocking queue based construction producer - consumer design, when the data is generated, the producer put data into a queue, and when the consumer is ready to process the data, to obtain data from the queue. Producers do not need to know the identity or number of consumers, or whether they are the only producers, but only the data can be put into the queue. Similarly, consumers do not need to know who the producer is or where it comes from work. BlockingQueue simplifies the producer - consumer design implementation process, it supports any number of producers and consumers. One of the most common producer - consumer model is designed to work in combination with the thread pool queue, Executor task execution framework will reflect this pattern.

When building high-reliability applications, bounded queue is a powerful resource management tool: they suppress and prevent excessive work items, the application has become more robust in the case of overloading.

Contained in the library in a number of implementations BlockingQueue, wherein, LinkedBlockingQueueand ArrayBlockingQueueis a FIFO queue, respectively, both with and ArrayList LinkedList similar, but have better than the synchronous List concurrent performance. PriorityBlockingQueue is a prioritized queue, when you want to follow a certain order to deal with the elements rather than FIFO, the queue will be very useful. As other ordered of containers, PriorityBlockingQueueboth elements can be compared according to the natural order of the elements (if they implement Comparable method), may be used to compare Comparator.

The last BlockingQueue implementation is SynchronousQueue, in fact it is not really a queue, because it does not maintain storage space in the queue elements. Different from the other queue is that it maintains a set of threads that are waiting for the elements added or removed from the queue.

Implemented in a variety of blocking queue in java.util.concurrent contains sufficient internal synchronization mechanism, thereby securely publish objects from producer threads to consumer threads.

  • 双端队列

Java 6 added two container types, Deque (pronounced "deck") and BlockingDeque, respectively for Queue and BlockingQueue been extended. Deque is a deque realized in Gao queue head and queue tail. Efficient insertion and removal. Specific implementations include ArrayDequeand LinkedBlockingDeque.
As suitable blocking queue producer - consumer model, the deque is equally applicable to other correlation pattern, i.e. the work of adhesion taken (Work Stealing). In producer - consumer design, all consumers have a shared work queue, taken in the design work of adhesion, each customer has its own deque. If a consumer to complete all the work himself double-ended queue, then it can get to work at the end of secretly from other consumers deque. Dense mode of operation than traditional take producer - consumer higher scalability mode, because the worker thread contention does not occur on a single shared task queue. Most of the time, they just visit their double-ended queue, thus greatly reducing the competition. When the worker thread needs to access another queue, it will be from the tail of the queue rather than obtaining work from the head, thus further reducing the degree of competition on the queue.
Take it ideal for close work both consumers and producers of the problem - when performing certain work may lead to more work occurs. For example, the web crawler in dealing with a page, usually will find more pages need to be addressed. FIG There are many similar search algorithms, such as marking the heap, Gao efficiency can be achieved by working in parallel adhesion mechanisms taking garbage collection phase. When a worker find a new job unit, it will put its own at the end of the queue (or work-sharing design mode, put another worker thread queue). When the double-ended queue is empty, it will look for a new job in another thread the tail of the queue, ensuring that each thread to keep busy.

四、阻塞方法与中断方法

The thread may block or suspend a variety of reasons: waiting for I / O operation, waiting to get a lock, waiting to wake up from Thread.sleep methods, or wait for the results of another thread. When a thread is blocked, it is usually suspended, and in some blocked (BLOCKED, WAITING or TIMED_WAITING). Blocking operation and a long execution time difference between an ordinary operation that blocked thread must wait after an event not under its control in order to continue to perform, such as waiting for I / O operation is complete, wait for a lock to become available, or wait for the end of the external computing. When an external event occurs, the thread is set back RUNNABLE state, and can be scheduled for execution again.

BlockingQueue of put and take methods will throw an exception under examination (Checked Exception) InterruptedException, which is the practice in some other ways the library of the same, for example Thread.sleep. When a method throws InterruptedException, indicating that the method is a blocking method, if the method is interrupted, it will advance efforts to end the blocking state.

Thread提供了 interrupt方法,用于中断线程或者查询线程是否已经被中断。每个线程都有 一个布尔类型的属性,表示线程的中断状态,当中断线程时将设置这个状态。

中断是一种协作机制。A thread can not force another thread to stop what it is doing away to perform other operations. When a thread A Interrupt B, A B is only required to stop operation is being executed in the execution to a place where you can pause - provided that if the thread B is willing to stop. Although not in the language or API specification is defined interrupt level for any particular application semantics, but the most commonly used interruptions is to cancel an operation. The method of interrupt request higher responsiveness, the easier the timely execution time is very long to cancel those operations.

当在代码中调用了一个将抛出InterruptedException异常的方法时,你自己的方法也就变成 了一个阻塞方法,并且必须要处理对中断的响应。对于库代码来说,有两种基本选择:

  • 传递InterruptedException. Avoid this anomaly is usually the most sensible strategy - simply pass the InterruptedException to the caller of the method. The method includes passing InterruptedException, do not catch the exception, or catch the exception, then after performing some simple cleanup This exception is thrown again.

  • 恢复中断。Sometimes not throw InteirraptedException, for example, when the code is part of Runnable time. In these cases, you must capture InterruptedException, and resume interrupted status by calling interrupt method on the current thread, so the call stack to see higher-level code will trigger an interrupt, as in the following program list.

public class TaskRunnable implements Runnable {
    @Override
    public void run() {
        try{

        } catch (InterruptedException e){
            Thread.currentThread().interrupt();
        }
    }
}

You can also use some more sophisticated interrupt handling method, but both methods can already handle most of the cases. 然而在出现InterruptedException时不应该做的事情是,捕获它但不做出任何响应。这将使调用栈上更高层的代码无法对中断采取处理措施,因为线程被中断的证据已经丢失。Only in a special case in order to shield interruption, ie Thread expand, and can control all higher-level code on the call stack.

五、同步工具类

The container class,阻塞队列是一种独特的类:它们不仅能作为保存对象的容器,还能协调生产者和消费者等线程之间的控制流,因为take和put等方法将阻塞,直到队列达到期望的状态 (队列既非空,也非满)。

同步工具类可以是任何一个对象,只要它根据其自身的状态来协调线程的控制流。Can be used as blocking queues synchronization tools, other types of tools further comprises a synchronization semaphore (Semaphore), the fence (Barrier) and latch (Latch).

All synchronization tools include certain structural properties: they encapsulate some states, which will determine the execution thread synchronization tools is to continue or wait, also provides methods of state operations, and other a method for efficiently enter waiting for synchronization tools to a desired state.

  • 闭锁

Atresia is a synchronization tools, you can delay the progress of threads until it reaches the final state.
闭锁的作 用相当于一扇门:在闭锁到达结束状态之前,这扇门一直是关闭的,并且没有任何线程能通过, 当到达结束状态时,这扇门会打开并允许所有的线程通过。当闭锁到达结束状态后,将不会再 改变状态,因此这扇门将永远保持打开状态。
Atresia can be used to ensure that certain activities until after the completion of other activities to continue, for example:

  • Ensure that a calculation only continue after all the resources it needs are initialized. Two yuan latch (two states) may be used to represent "resource has been initialized R ', R and all required operations must wait on this latch.
  • To ensure that a service was started after its dependent all other services have started. Each service has an associated binary atresia. S when starting the service, the first in the S-dependent blocking other services wait, after all dependent services are started will release the lockout S, S so that other dependent services to continue.
  • Wait until all participants of an operation (for example, all players in the multi-player game) are in place before proceeding. In this case, when all players are ready, the lockout will reach the end state.

CountDownLatch是一种灵活的闭锁实现,可以在上述各种情况中使用,它可以使一个或 多个线程等待一组事件发生。闭锁状态包括一个计数器,该计数器被初始化为一个正数,表示 需要等待的事件数量。countDown方法递减计数器,表示有一个事件已经发生了,而await方 法等待计数器达到零,这表示所有需要等待的事件都已经发生。如果计数器的值非零,那么 await会一直阻塞直到计数器为零,或者等待中的线程中断,或者等待超时。

In the program list TestHamess class shows two common uses atresia. TestHamess create a certain number of threads, use them concurrently perform assigned tasks. It uses two locking, represent "start gate (Starting Gate)" and "End gate (Ending Gate)". The initial value of the counter start gate 1, the initial value of the number of the working end of the thread of the counter door. Each worker thread value is the first thing to do is wait on the boot door, ensuring that all the threads are in place before you begin. And each thread is the last thing to do is to call the end method countDown door minus 1, which enables the main thread to wait until all the work efficiently execute threads are completed ,, so you can count the time consumed.

public class TestHarness {

    public long timeTasks(int nThreads, final Runnable task) throws InterruptedException {
        final CountDownLatch startGate = new CountDownLatch(1);
        final CountDownLatch endCate  = new CountDownLatch(nThreads);
        for (int i = 0; i < nThreads; i++){
            Thread t = new Thread(){
              public void run(){
                  try {
                      startGate.await();

                      try{
                          task.run();
                      }finally {
                          endCate.countDown();
                      }
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
            };

            t.start();
        }
        long start = System.nanoTime();
        startGate.countDown();
        endCate.await();
        long end = System.nanoTime();
        return end - start;
    }

}
  • FutureTask

FutureTask也可以用做闭锁。(FutureTask实现了 Future语义,表示一种抽象的可生成结果的计算。)FutureTask表示的计算是通过Callable来实现的,相当于一种可生 成结果的Runnable,并且可以处于以下3种状态:等待运行(Waiting to nm),正在运行 (Running)和运行完成(Completed)。“执行完成”表示计算的所有可能结束方式,包括正 常结束、由于取消而结束和由于异常而结束等。当FutureTask进入完成状态后,它会永远 停止在这个状态上。

Future.get的行为取决于任务的状态。如果任务已经完成,那么get会立即返回结果,否则 get将阻塞直到任务进入完成状态,然后返回结果或者抛出异常。FutureTask the computation result from the implementation of computing threads to get this result thread, FutureTask norms to ensure that this transfer process can achieve safe release of results.

Executor FutureTask indicating an asynchronous frame in the task, can also be used in addition represent long computation some time, these calculations can be started before the results. Listing in the Preloader FutureTask on the use of a high overhead to perform the calculation, and the results will be used later. Calculated by starting early, you can reduce the time required while waiting for the results.

public class Preloader {

    private final FutureTask<ProdunctInfo> future = new FutureTask<>(new Callable<ProdunctInfo>() {
        @Override
        public ProdunctInfo call() throws Exception {

            return loadProductInfo();
        }
    });

    private final Thread thread = new Thread(future);

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

    public ProdunctInfo get() {
        try{

            ProdunctInfo produnctInfo = future.get();
            System.out.println("执行成功");
            return produnctInfo;
        } catch (ExecutionException | InterruptedException e){
            e.printStackTrace();
        }
        return null;
    }


    public ProdunctInfo loadProductInfo(){
        return new ProdunctInfo();
    }


    public static void main(String[] args) {
        Preloader preloader = new Preloader();

        preloader.start();
        
        ProdunctInfo produnctInfo = preloader.get();
        System.out.println(produnctInfo.getClass());

    }

}
  • 信号量

计数信号量(Counting Semaphore)用来控制同时访问某个特定资源的操作数量,或者同时 执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界。

Semaphore managed with a set of virtual license (permit), the initial number of licenses can be specified by the constructor. When performing operations can first be licensed (as long as there is a surplus of licenses), and release the license later use. If there is no license, acquire will block until the license (or until the operation is interrupted or timed out). method returns a release permission to the semaphore. A simplified form of the computing semaphore is a binary semaphore, i.e., the initial value of the Semaphore 1 "binary semaphore can be used mutex (the mutex), and includes a non-reentrant locking semantics: who owns this unique license, will have a mutex.

Semaphore可以用于实现资源池,例如数据库连接池。We can construct a fixed length of the resource pool when the pool is empty, the resource request will fail, but you really want to see rather than failure behavior is blocked, and unblocked when the pool is not empty. If Semaphore count value is initialized to the size of the pool, and calls acquire method before obtaining a resource from the pool first obtain a license, the license is released after the call to release resources back to the pool, then acquire will remain blocked until the resource pool is not Is empty. In constructing the blocking object pool, a simpler method is to use BlockingQueue to save the pool of resources. )

Similarly, you can also use Semaphore to become any kind of container bounded blocking container, as shown in the program list BmmdedHashSet. The count value of the semaphore is initialized to the maximum capacity of the container. add operation prior to addition to the bottom of a container element, first obtain a license. If the add operation does not add any element, then immediately release the license. Similarly, a Remove operation of the release permission, so that more elements can be added to the vessel. Set underlying implementation does not know anything about the border, which is handled by the BoundedHashSet.

public class BoundedHashSet<T> {
    private final Set<T> set;
    private final Semaphore sem;


    public BoundedHashSet(int bound){
        this.set = Collections.synchronizedSet(new HashSet<>());
        sem = new Semaphore(bound);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
    }

    public boolean add(T o) throws InterruptedException {
        sem.acquire(); // 阻塞直到有许可
        boolean wasAdded = false;
        try{
            wasAdded = set.add(o);
            return wasAdded;
        }
        finally {
            if (!wasAdded){
                sem.release(); // 返回一个许可给信号量
            }
        }
    }

    public boolean remove(Object o){
        boolean wasRemoved = set.remove(o);
        if (wasRemoved) {
            sem.release(); // 返回一个许可给信号量
        }
        return wasRemoved;
    }
}
  • 栅栏

栅栏(Barrier)类似于闭锁,它能阻塞一组线程直到某个事件发生. The key difference between the fence and the lockout is that all threads must arrive at the same location of the fence, in order to continue. Lockout wait for the event, and the fence waiting for the other thread. Fence used to implement some of the agreements, for example, several families decided somewhere collection:. "Everyone meet at 6:00 at McDonald's, to wait for the future to other people, and then talk to do next."

CyclicBarrierCan make a number of participants in the fence repeatedly position together, it is very useful for parallel iterative algorithm: This algorithm will typically split a problem into a series of independent sub-problems. 当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。If all the threads have reached the location of the fence, the fence will then open, then all threads are released, and the fence will be reset for the next use. If you call a timeout to await, or await blocked thread is interrupted, then the fence is considered broken, all the blocked await call will terminate and throw BrokenBarrierException. If you successfully through the fence, then each thread will await returns a unique index number arrives, we can use these indexes to "election" to produce a leading thread and the next iteration to perform some special work by the leadership of the thread. CyclicBarrier can also make you a fence operation passed to the constructor, which is a Runnable, will (in a sub-task thread) to perform when it successfully passed the fence, but was released before blocking the thread can not be executed.

Another form of a fence Exchanger, it is a two-party (Two-Party) fence, the fence parties exchanging data position. When two asymmetric side performs operation, Exchanger can be useful, for example when one thread writes data to the buffer, and another thread to read data from the buffer. These threads can be used Exchanger to converge, and full buffer and empty buffer exchange. When two threads through Exchanger exchange objects, this exchange took these two objects safely released to another party.


Guess you like

Origin blog.csdn.net/qq_27870421/article/details/90582817