Senior programmer concurrent programming knowledge (b) of Notes

Explanation

Benpian is the second on a concurrent programming content not finished discussing the sequel. Previous Portal:

Java Concurrency a million words sum up (vomiting blood finishing)

Active issues

When we discuss the risks on a concurrent programming brought about by, a risk which is active when it comes to sexual issues. Active issue is actually our program do not go in some scenarios or conditions. Under this topic we will get to know what is deadlock, livelock and starvation, how to avoid these situations.

Deadlock

We generally use locking to ensure thread-safe, but excessive use of locking and can lead to deadlock.

Dining Philosophers

"Dining philosophers" problem can be well described deadlock scenarios. 5 philosopher eat hot pot, sitting on a round table. They have five chopsticks (not five pairs), which 5 in the middle of each of chopsticks. Philosophers sometimes thinking, sometimes a meal. Each person must take to a pair of chopsticks to eat something, and after eating chopsticks back in place.

Here Insert Picture Description
Consider the case may be, if everyone jumped at the left of their own chopsticks, chopsticks and then wait for the right of their own vacant, but he has not let go to get the chopsticks. What would happen. It is contemplated that everyone have to eat hot pot, just waiting for the cool.

What is a deadlock

Everyone has the resources others need, while others have to wait for resources owned, and each person before obtaining the required resources will not give up resources already have. This is a kind of deadlock.

Then use the term to describe the thread. A thread holds the lock while L and M would like to acquire a lock, the thread B holds the lock M L and try to get a lock, so two threads will forever wait.

Deadlock simple code sample

public class LeftRightDeadLock {
    private final Object left = new Object();
    private final Object right = new Object();
    
    public void leftRight(){
        synchronized (left){
            synchronized (right){
                doSomething();
            }
        }
    }
    
    public void rightLeft(){
        synchronized (right){
            synchronized (left){
                doSomething();
            }
        }
    }
}

The above code, if a thread of execution LeftRight () method, another thread calls rightleft () method, a deadlock occurs.

Deadlock above production reasons, the two threads of different views in order to obtain the same lock. If the lock request in the same order, then it will not appear dependent locked loop, therefore no deadlock.

Generating four conditions deadlock

A man named Coffman cattle to help us summarize the four conditions of deadlock:

  1. Mutually exclusive, shared resources X and Y can only be occupied by a thread
  2. Occupancy and waiting, the thread T1 has gained share resources X, Y while waiting for a shared resource, and does not release a shared resource X;
  3. Can not seize, other threads can not be forced to seize the resources occupied by the thread T1;
  4. Loop waiting, waiting for a resource thread thread T1 T2 possession, waiting thread thread T2 T1 possession of resources, is the circular wait.

Conversely, we just destroyed a four conditions, you can avoid the occurrence of deadlock.

First, the first condition can not damage a mutex because the mutex is locked semantics.

  1. For the "occupation and wait" conditions, we can apply all the resources one time;
  2. For the "not seize" this condition, the thread take up some of the resources of other resources in the application, if the application is not visible, it can take the initiative to release the resources occupied.
  3. For the "cycle of waiting," this condition, you can apply for funding for a fixed order, all threads get locked in accordance with the provisions of the order, so there is no waiting for the cycle.

Livelock

Livelock is another form of active issues, even though the problem does not block the thread, but they can not continue to go on, because the thread will continue to repeat the same operation, and will always fail.

When a plurality of cooperating thread in response to each other to modify the respective states, and such that any thread can proceed, livelock occurs. For example, two people meet on the road, out of courtesy, gave each other to give way, the results always come together.

To solve this problem of living locks, necessary to add randomness in the retry mechanism. For example, in the network, both machines using the same carrier transmits data packet, then the packet collision occurs. These machines are checked to the conflict, and are retransmitted again later. If they choose to retry after one second, then the conflict will happen and keep the conflict going, even when there is a lot of idle bandwidth, it can not send out a packet. To avoid this from happening, they need to wait a random time, respectively, so as to avoid livelock occurred.

hunger

"Hunger" is due when the thread can not access the resources it needs and can not continue the scene. The so-called "inequality is not scarcity." When some threads have been acquired less CPU resources to perform when it happened, "Hunger."

Some scenes easily lead to hunger:

  1. In applications improper use of Java thread priority. (Because the JVM Thread API will be in the 10 priority mapping to the scheduling priorities of the operating system, which may exist with a priority of two different priorities are mapped to the operating system layer, so try not to change the thread priority)
  2. Thread holding the lock, or if the time during the execution of an infinite loop, it could lead to "hunger" problem.

Solve the "hunger" problem of the general scheme is to use a fair lock (Note that the term non-synchronized fair locks).

JUC tool library

Java Concurrency contracted we offer a very rich base module build concurrent programs, such as thread-safe container classes, synchronization tools, such as blocking queue.

These tools are java.util.concurrent package below, so referred JUC kit.

Synchronization and concurrency container vessel

What type synchronous container

Vector and mainly the Hashtable, both of which are the early part of the JDK, and some are created by the wrapper class factories Collections.sychronizedXxx method.
These classes are thread-safe way to achieve are: encapsulate their state, and each public methods are synchronized, that is, use the built-in lock synchronized manner, so that the state that only one thread can access the container.

Synchronization issues container class

Although the synchronous container class is thread-safe class, but in some scenarios may require additional client thread lock to protect the safety of the combined operation. Iterative example (repeated access element until completion of the traversing all elements of the container), operation conditions (if not add). The following description gives an example below:

public class GetLastElement implements Runnable{
    private List<Object> list;

    public GetLastElement(List<Object> list) {
        this.list = list;
    }

    public Object getLast(){
        int index = list.size() - 1;
        return list.get(index);
    }

    public void run() {
        getLast();
    }
}

public class RemoveLastElement implements Runnable{
    private List<Object> list;

    public RemoveLastElement(List<Object> list) {
        this.list = list;
    }

    public void deleteLast(){
        int index = list.size() - 1;
        list.remove(index);
    }

    public void run() {
        deleteLast();
        System.out.println(list);
    }

    public static void main(String[] args) {
        List<Object> list = new Vector<Object>();
        list.add(123);
        list.add("hello");

        Thread thread1 = new Thread(new GetLastElement(list));
        Thread thread2 = new Thread(new RemoveLastElement(list));
        thread1.start();
        thread2.start();
    }
}

The above code, may appear at the time of acquisition thread 1 get (2), the elements on the thread 2 2 position has been to remove (2) out. At this time, thread 1 will throw an exception, but this is not what we expect the results obtained.

Therefore, in order to ensure the atomicity of the combined operation, it is necessary in the lock and remove get method. Like this:

    public Object getLast(){
        synchronized (list){
            int index = list.size() - 1;
            return list.get(index);
        }        
    }

    public void deleteLast(){
        synchronized (list){
            int index = list.size() - 1;
            list.remove(index);
        }
    }

This ensures that calls between size () and get () method, not an opportunity for another thread coming off remove the element.

Iterators and ConcurrentModificationException

Java class libraries in the set are achieved Iterator iterator interface. Whether using a standard iterative cycle is for-each syntax further, iterative traversal of containers manner are Iterator. However, if there are other threads concurrently modify the vessel, even if it is using an iterator can not avoid container lock during iteration.

Fast failure (fail-fast) mechanism

The so-called fail-fast, in fact, is a kind of warning mechanism, that is, when they found the container is modified in an iterative process that ConcurrentModificationException will throw an exception.

For example, the following code:

        List<String> list = new Vector<String>();
        list.add("java");
        for(String str : list){
            list.remove(str);
        }

ConcurrentModificationException runtime will throw an exception, in fact we replaced ArrayLlist also exist as such fail-fast mechanism.

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.Vector$Itr.checkForComodification(Vector.java:1184)
	at java.util.Vector$Itr.next(Vector.java:1137)
	at thread.syncontainer.FailFast.main(FailFast.java:10)

Another point, synchronous container class all access to the container status are serialized, in order to achieve thread safety, but this method severely reduces concurrency when multiple threads compete lock container throughput will seriously reduce.

Concurrent containers

Providing a variety of performance problems Java5 concurrent containers used to improve the synchronization of the container.

  • Increased ConcurrentHashMap, synchronized and used in place of a hash-based Map;
  • CopyOnWriteArrayList, traversal operation for the case where the main operation is synchronized List instead.
  • Added Queue and BlockingQueue;
  • And the introduction of ConcurrentSkipListMap Java6 and ConcurrentSkipListSet, respectively as synchronized concurrent SortedSet SortedMap and alternatives.

ConcurrentHashMap

Like with HashMap, ConcurrentHashMap is a hash-based Map, but it uses a different locking strategy to provide higher concurrency and scalability. ConcurrentHashMap not every method in the same synchronization 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 becomes segmented lock. In this mechanism, multiple threads can read concurrent access Map, read and write threads threads can also be accessed concurrently Map. And a number of write threads can concurrently modify Map. The results ConcurrentHashMap bring is to achieve higher throughput in concurrent access environment.

Meanwhile ConcurrentHashMap provided ConcurrentModificationException iterator does not throw an exception. Another point is to achieve the ConcurrentMap, the interface provides a composite operation such as "If not add", "if equal, to remove" "Alternatively, if equal," and the like, and is guaranteed to be an atomic operation.

CopyOnWriteArrayList

"Copy (copy-on-write) when writing" thread safety containers is that, as long as the right to publish the fact that immutable objects, then there is no further synchronization at that time to access the object. At each modification, and re-release will create a new copy of container, in order to achieve variability.

Blocking queue

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 method blocks until space is available; if the queue is empty, then take the method will block until the element is available. Bounded queue can also be unbounded, unbounded queue will never be filled, so unbounded put method on the queue will never be blocked.

Java class libraries in blocking queue BlockingQueue is an interface, it has a variety of implementations, which are ArrayBlockingQueue LinkedBlockingQueue and FIFO (First In First Out) queue, respectively, and both LinkedList and ArrayList is similar to, but better than the synchronous List have concurrent performance. PriorityBlockingQueue is a prioritized queue, when you want to follow a certain order to deal with instead of FIFO elements, you can use this queue.

Based blocking queue can be very easily achieved producer - consumer model. When the data is generated, the producers just put the data into the blocking queue, and consumer consumption data from the queue to get on it. Compared wait / notify implement much simpler.

CountDownLatch

CountDownLatch is a locking implementation. So what is blocking it? Progress is a lockout synchronization tools, threads can be delayed until it reaches the final state. Atresia is equivalent to a door: Before the lockout reaches the end state, the door has been closed, and no thread through when it reaches the end of the state (in general is counter to 0), the door will open and allow all threads pass.

You can set the count value to an initial target CountDownLatch, any calls to the await () method on the object are blocked, this count value is 0 to know. While other task (or thread) at the end of its work, you can call countDown () method on the object to reduce the counter value.

Also to note is that, CountDownLatch is designed to be triggered only once, which means after the counter reaches zero will not be reset.

See a map point more clearly:

Here Insert Picture Description

We can see Thread-0 calls await on CountDownLatch entity object () method after block until the other two threads will run counter count was reduced to 0, Thread-0 thread before continuing execution.

CountDownLatch typical use of a task is to be divided into N independent sub-tasks, each task executed by the N sub N threads, to create an initial value of N CountDownLatch locking, the main thread calls the await () method blocks waiting for the results of subtask , child thread in executing the task is to call countDown down counter values.

public class TaskPortion implements Runnable{
    private static int counter = 0;
    private final int id = counter++;
    private final CountDownLatch countDownLatch;

    public TaskPortion(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    public void run() {
        doWork();
        countDownLatch.countDown();
    }

    public void doWork(){
        System.out.println(this + "完成");
    }

    @Override
    public String toString() {
        return "TaskPortion{" +
                "id=" + id +
                '}';
    }
}

public class MainTask implements Runnable{
    private CountDownLatch countDownLatch;

    public MainTask(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    public void run() {
        try {
            countDownLatch.await();
            System.out.println("主任务完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for(int i = 0; i < 10; i++){
            exec.execute(new TaskPortion(countDownLatch));
        }
        exec.execute(new MainTask(countDownLatch));
        exec.shutdown();
    }
}

operation result:

TaskPortion{id=1}完成
TaskPortion{id=3}完成
TaskPortion{id=0}完成
TaskPortion{id=5}完成
TaskPortion{id=9}完成
TaskPortion{id=4}完成
TaskPortion{id=8}完成
TaskPortion{id=6}完成
TaskPortion{id=2}完成
TaskPortion{id=7}完成
主任务完成

When you can see all the subtasks have completed the main task to complete.

CyclicBarrier

Fence (Barrier) similar to the lockout, it clogged a set of threads until an event. 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.

CyclicBarrier is an implementation of the barrier, and can be determined from the name Cycilc is recycled fence (Barrier). It is suitable for this scenario: you want to create a set of tasks that are executed in parallel work, then before making the next step wait until all tasks are completed. It makes all the parallel tasks are lined up at the fence, it can be moved forward in unison.

Examples of "Java programming ideas" in the horse racing game as an example the following references:

public class Horse implements Runnable{
    private static int counter = 0;
    private final int id = counter++;
    private int strides = 0;
    private static Random rand = new Random(7);
    private static CyclicBarrier barrier;
    public Horse(CyclicBarrier b){ barrier = b; }
    public synchronized int getStrides(){  return strides; }

    public void run() {
        try {
            while (!Thread.interrupted()){
                synchronized (this){
                    strides += rand.nextInt(3);
                }
                barrier.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    public String toString(){ return "Horse " + id + " "; }

    public String tracks(){
        StringBuilder s = new StringBuilder();
        for(int i = 0; i < getStrides(); i++ ){
            s.append("*");
        }
        s.append(id);
        return s.toString();
    }
}

public class HorseRace {
    static final int FINISH_LINE = 15;
    private List<Horse> horses = new ArrayList<Horse>();
    private ExecutorService exec = Executors.newCachedThreadPool();
    private CyclicBarrier barrier;

    public HorseRace(int nHorses, final int pause){
        barrier = new CyclicBarrier(nHorses, new Runnable() {
            public void run() {
                StringBuilder s = new StringBuilder();
                for(int i = 0; i< FINISH_LINE; i++){
                    s.append("=");
                }
                System.out.println(s);

                for(Horse horse : horses){
                    System.out.println(horse.tracks());
                }
                for(Horse horse : horses){
                    if(horse.getStrides() >= FINISH_LINE){
                        System.out.println(horse + "won!");
                        exec.shutdownNow();
                        return;
                    }
                }

                try {
                    TimeUnit.MILLISECONDS.sleep(pause);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        for (int i = 0; i < nHorses; i++){
            Horse horse = new Horse(barrier);
            horses.add(horse);
            exec.execute(horse);
        }
    }

    public static void main(String[] args) {
        int nHorses = 7;
        int pause = 200;
        new HorseRace(nHorses,pause);
    }
}

This program is very interesting, and here I put a small number of horses adjusted to three horses, some of FINISH_LINE transfer of small values, the results are as follows:

===============
*0
1
**2
===============
**0
*1
***2
===============
****0
*1
****2
===============
****0
*1
****2
===============
****0
**1
*****2
===============
****0
**1
******2
===============
*****0
**1
******2
===============
*******0
***1
******2
===============
*********0
*****1
******2
===============
***********0
*****1
*******2
===============
***********0
******1
*******2
===============
***********0
******1
*********2
===============
***********0
******1
**********2
===============
*************0
********1
************2
===============
*************0
********1
*************2
===============
*************0
*********1
**************2
===============
*************0
*********1
***************2
Horse 2 won!

CyclicBarrier constructor has two parameters, a first number of tasks in parallel, the second type of fence is a Runnable task is when we counter is reduced to 0, the barrier automatically performs this task.

Decrement counter action here is not what we do (the code we just call CyclicBarrier the await () method), but CyclicBarrier automatically help us do it, there is a count field in CyclicBarrier by watching the following comment when we all know parallel threads are arriving at the fence, and will be reduced to 0, and then will be reset at the start of a new round.

    /**
     * Number of parties still waiting. Counts down from parties to 0
     * on each generation.  It is reset to parties on each new
     * generation or when broken.
     */
    private int count;

Semaphore

Normal lock (or lock Lock display built Jvm lock the synchronized) at any time only one task to allow access to a resource, and the counting semaphores (Semaphore) n tasks simultaneously allowing access to this resource.

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 () method blocks until one license (or until the operation is interrupted or timed out). release () method returns a permission to the semaphore.

Semaphore can generally be used to implement the " object pool ", which manages a limited number of objects, objects can be used when they checked out, and when the user is finished, they can sign back.

public class Pool<T> {
    private int size;
    private List<T> items = new ArrayList<T>();
    private volatile boolean [] checkedOut;
    private Semaphore available;
    public Pool(Class<T> classObject, int size){
        this.size = size;
        checkedOut = new boolean[size];
        available = new Semaphore(size,true);
        for (int i = 0; i < size; i++){
            try {
                items.add(classObject.newInstance());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    public T checkOut() throws InterruptedException {
        available.acquire();
        return getItem();
    }
    public void checkIn(T x){
        if(releaseItem(x)){
            available.release();
        }
    }
    
    private synchronized T getItem(){
        for (int i = 0; i < size; i++){
            if(!checkedOut[i]){
                checkedOut[i] = true;
                return items.get(i);
            }
        }
        return null;
    }
    
    private synchronized boolean releaseItem(T item){
        int index = items.indexOf(item);
        if(index == -1) return false;
        if(checkedOut[index]){ 
            checkedOut[index] = false;
            return true;
        }
        return false;
    }
}

Thread Pool

Do not create a wild thread

This creates a thread as below, we called the "wild thread", as a test demo is no problem, if you want to produce on the system, it is best not to do so.

Thread t = new Thread(r)//r是runnable对象

Because of this to create a thread, it is likely to cause the thread to create unlimited, and thread management is very inconvenient.

We know that thread creation overhead is relatively high, so the thread needs multiplex, pool management needs.

Executor framework

Executor framework is actually a Java class library available to our task management framework, the thread pool is part of it.

In the Java class libraries, a task performed mainly abstract than Thread, but Executor. Executor is an interface, as follows:

public interface Executor {
    void execute(Runnable command);
}

Executor it provides a standard way to perform a task with the submission process Decoupling off, and is represented by Runnable task. Executor implementation class also provides support for life-cycle, and statistics collection, management mechanism and application performance monitoring mechanisms.

After the submission and execution of tasks decoupling off, we have the flexibility to specify the tasks and modify some type of strategy execution. Defines the tasks performed in all aspects of the implementation of the policy, including:

  • Perform tasks in what thread?
  • In what order to perform the task (FIFO, LIFO, priorities)?
  • How many tasks can execute concurrently?
  • How many tasks awaiting execution in the queue?
  • If the system due to overload need to reject a task, then the task of selecting which one? Also, notice how the application has been rejected assignment?
  • Before or after performing a task, what action should be?

Create a thread pool through the static factory method in the Executors utility class

We must first know Executors is a utility class, it is to help us to create some common thread pool thread pool which provides some default configuration.

For example, we can create the following several common thread pool by Executors of static factory method:

newFixedThreadPool

Thread pool that creates a fixed-length, every time you submit a task to create a thread, until the maximum number of threads in the pool, then the size of the thread pool will not change.

newCachedThreadPool

You can create a cached thread pool, if the current size of the thread pool exceeds the needs of blow, it will recover idle thread, and when demand increases, you can add a new thread, the thread pool size of the absence of any restrictions.

newSingleThreadExecutor :

Executor is a single-threaded, it creates a single worker thread to perform the task, if the thread ends abnormally, it creates another thread instead.

ExecutorService interface provides life-cycle management approach

ExecutorService is an interface, inherited from the Executor, extends the functionality of Executor interface provides methods of lifecycle management services:

public interface ExecutorService extends Executor {

    /**
     * 关闭执行器, 主要有以下特点:
     * 1. 已经提交给该执行器的任务将会继续执行, 但是不再接受新任务的提交;
     * 2. 如果执行器已经关闭了, 则再次调用没有副作用.
     */
    void shutdown();

    /**
     * 立即关闭执行器, 主要有以下特点:
     * 1. 尝试停止所有正在执行的任务, 无法保证能够停止成功, 但会尽力尝试(例如, 通过 Thread.interrupt中断任务, 但是不响应中断的任务可能无法终止);
     * 2. 暂停处理已经提交但未执行的任务;
     *
     * @return 返回已经提交但未执行的任务列表
     */
    List<Runnable> shutdownNow();

    /**
     * 如果该执行器已经关闭, 则返回true.
     */
    boolean isShutdown();

    /**
     * 判断执行器是否已经【终止】.
     * <p>
     * 仅当执行器已关闭且所有任务都已经执行完成, 才返回true.
     * 注意: 除非首先调用 shutdown 或 shutdownNow, 否则该方法永远返回false.
     */
    boolean isTerminated();

    /**
     * 阻塞调用线程, 等待执行器到达【终止】状态.
     *
     * @return {@code true} 如果执行器最终到达终止状态, 则返回true; 否则返回false
     * @throws InterruptedException if interrupted while waiting
     */
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

    /**
     * 提交一个具有返回值的任务用于执行.
     * 注意: Future的get方法在成功完成时将会返回task的返回值.
     *
     * @param task 待提交的任务
     * @param <T>  任务的返回值类型
     * @return 返回该任务的Future对象
     * @throws RejectedExecutionException 如果任务无法安排执行
     * @throws NullPointerException       if the task is null
     */
    <T> Future<T> submit(Callable<T> task);

    /**
     * 提交一个 Runnable 任务用于执行.
     * 注意: Future的get方法在成功完成时将会返回给定的结果(入参时指定).
     *
     * @param task   待提交的任务
     * @param result 返回的结果
     * @param <T>    返回的结果类型
     * @return 返回该任务的Future对象
     * @throws RejectedExecutionException 如果任务无法安排执行
     * @throws NullPointerException       if the task is null
     */
    <T> Future<T> submit(Runnable task, T result);

    /**
     * 提交一个 Runnable 任务用于执行.
     * 注意: Future的get方法在成功完成时将会返回null.
     *
     * @param task 待提交的任务
     * @return 返回该任务的Future对象
     * @throws RejectedExecutionException 如果任务无法安排执行
     * @throws NullPointerException       if the task is null
     */
    Future<?> submit(Runnable task);

    /**
     * 执行给定集合中的所有任务, 当所有任务都执行完成后, 返回保持任务状态和结果的 Future 列表.
     * <p>
     * 注意: 该方法为同步方法. 返回列表中的所有元素的Future.isDone() 为 true.
     *
     * @param tasks 任务集合
     * @param <T>   任务的返回结果类型
     * @return 任务的Future对象列表,列表顺序与集合中的迭代器所生成的顺序相同,
     * @throws InterruptedException       如果等待时发生中断, 会将所有未完成的任务取消.
     * @throws NullPointerException       任一任务为 null
     * @throws RejectedExecutionException 如果任一任务无法安排执行
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;

    /**
     * 执行给定集合中的所有任务, 当所有任务都执行完成后或超时期满时(无论哪个首先发生), 返回保持任务状态和结果的 Future 列表.
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;

    /**
     * 执行给定集合中的任务, 只有其中某个任务率先成功完成(未抛出异常), 则返回其结果.
     * 一旦正常或异常返回后, 则取消尚未完成的任务.
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;

    /**
     * 执行给定集合中的任务, 如果在给定的超时期满前, 某个任务已成功完成(未抛出异常), 则返回其结果.
     * 一旦正常或异常返回后, 则取消尚未完成的任务.
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Detailed thread pool ThreadPoolExecutor

Calculate the size of the thread pool

Ideal thread pool size depends on the type of job being submitted and the nature of the deployment of the system. In the code does not usually hard-coded to write the death of one value, and should be provided by some configuration mechanism.

To avoid too large or too small. Set too high, then the large number of threads will compete occur on relatively little CPU and memory resources, it will not only result in higher memory usage, but also may run out of resources. If set too low, it will cause a lot of idle processor can not perform work, waste of resources, thus reducing throughput.

"Concurrency in Practice" in the experience provided me with a formula:

If the processor utilization to achieve the desired optimal size of the thread pool is equal to:

  Nthreads = Ncpu * Ucpu * (1 + W / C), wherein

  Ncpu = CPU cores

  Ucpu = CPU utilization, 0 to 1

  The ratio W / C = latency and computation time

CPU number can be obtained by Runtime:

int N_CPUS = Runtime.getRuntime().availableProcessors();

配置ThreadPoolExecutor

NewFixedThreadPool we created earlier by the Executors utility class factory method, newCachedThreadPool these thread pools, pool their return is ThreadPoolExecutor object.

Here Insert Picture Description

When the default execution policy can not meet demand, we will be able to customize your own thread pool by ThreadPoolExecutor.

We look at ThreadPoolExecutor constructor:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

A total of four overloaded constructor, we choose the most analyzes parameters, several other calls are on top of this.

Look at the following parameters:

corePoolSize : shows the basic size of the thread pool, also known as the core number of threads, and when the work queue is full time, if the author has come up in the task, will create more than this number of threads.

maximumPoolSize : maximum number of threads that the thread pool can create up so many threads.

keepAliveTime : survival time of idle threads, if a thread is idle for more than the survival time will be recycled.

Unit : survival time per unit time.

workQueue : blocking queue, the task is to submit up if there is no available thread execution, will be placed in the queue waiting to be executed.

threadFactory : a thread factory used to specify how to create a thread, for example, you can specify a meaningful name, specify a UncaughtExceptionHandler and so on.

RejectedExecutionHandler : denial policy or strategy saturation processor. When the bounded queue is full, how to deal with the task of submitting up. JDK provides four RejectedExecutionHandler implementation. AbortPolicy, DiscardPolicy, DiscardOldestPolicy and CallerRunsPolicy.

AbortPolicy saturation strategy is the default strategy, which will throw an unchecked RejectedExecutionExcetion exception. The caller can catch exceptions be treated accordingly.

DiscardPolicy strategy will abandon the task, under DiscardOldestPolicy will abandon a shape to be back to the task, and then try to re-submit a new task.

CallerRunsPolicy not abandon the task, it will not throw an exception, but will fall back to the caller certain tasks, thereby reducing the flow of new tasks.

The following diagram depicts the overall workflow thread pool:

Here Insert Picture Description

JAVA memory model

Java memory model is a very complex specification, this brief introduction.

Java memory model specifies all the variables are stored in main memory, each thread also has its own working memory, working memory holds the thread in the thread is a copy of a copy of main memory used variables, variable thread All operations must be carried out in the working memory, but can not directly read and write main memory. Between different threads can not directly access the working memory of the other variables, the variables are passed between threads require data between their working memory and main memory simultaneously.

JMM is a specification, in order to address when communicating because multithreading, local memory data inconsistency exists through shared memory, the compiler will reorder code instructions, codes will bring the processor out of order execution problem. Aim is to ensure atomicity concurrent programming scene, visibility and orderliness.

happen-before rule

happen-before the rules are rules JMM restriction instruction reordering. Means that the subsequent operation is a visible result of the preceding operation of. It came up with six happen-before rules:

1, the program sequence rules.

Means that the foregoing procedure to modify a variable must be visible for a subsequent operation.

    public void read(){
        int x = 20;
        boolean b = true;
    }

= 20 the X- happen-the before b = to true, which is consistent with our intuition.

2, volatile variable rules

Of volatile white you two write operations happen-before read in this variable. This is also volatile variable to ensure that the cause of visibility.

3, transfer rules

如果A happen-before B,B happen-before C。则A happen-before C。

4, the monitor lock rules

Monitor lock refers synchronized. Lock to unlock a lock to happen-before the follow-up to this lock.

    public void read(){
        int x = 20;
        boolean b = true;
        synchronized (this){// 这里会自动加锁
            x = 10;
        }//执行结束,自动释放锁
    }

This rule says is that, if executed after completion of thread A releases the lock changed to 10 x the thread B after obtaining the lock to enter the critical area is the thread can be seen on x A write operation, it is able to see B x = 10.

5, the thread start rule

It refers to the main thread after thread B A promoter, child thread B can see the operation before the main thread thread B promoter.

        int x = 10;
        Thread B = new Thread(()->{
            // 此处是可以看到主线程修改的x=30的
        });
        x = 30;
        B.start();

6, the thread join rules

This article is about the waiting threads. It refers to the main thread A waits for the child thread B is completed (the main thread A thread the Join B by calling sub () method), when the child thread B is completed (the main thread A in the join () method returns), the main thread is able to look to operate the sub-thread.

        int x = 10;
        Thread B = new Thread(()->{
            // 对共享变量的修改
            x = 15;
        });
        B.start();
        B.join();
        
        // 主线程在调用了join之后是能
        // 看到x = 15的

postscript

Late something CAS principle, AQS and explicit principle of lock ReentrantLock summary alone.

Guess you like

Origin www.cnblogs.com/happyone/p/12591396.html