thread

Concurrency doesn’t necessarily depend on multithreading, but most discussions about concurrency in Java are about threads. Thread is a more lightweight scheduling execution unit than process. The introduction of thread can separate the resource allocation and execution scheduling of a process. Each thread can share process resources (memory address, file IO, etc.), and can be independent scheduling (thread is the basic unit of CPU scheduling). All key methods of the Thread class are declared native, meaning that the method is not implemented or cannot be implemented using platform-independent means, possibly for execution efficiency.

Java thread scheduling

Thread scheduling refers to the process in which the system allocates processor rights to threads. There are two main scheduling methods, namely cooperative thread scheduling and preemptive thread scheduling. Collaborative thread scheduling, thread execution time by the thread itself to control, the thread to finish their own work, to take the initiative to inform the system to switch to another thread. The biggest advantage is that the implementation is simple, and the switching operation is known to the thread itself, there is no thread synchronization problem. The downside is that thread execution time is out of control, and if a thread has a problem, it can stay blocked. In preemptive scheduling, each Thread is allocated execution time by the system, and Thread switching is not determined by the Thread itself. (In Java, Thread.yield() yields execution time, but does not obtain execution time.) Thread execution time is controlled by the system, and no single thread will cause the entire process to block. **Java thread scheduling is preemptive scheduling. ** You want the system to allocate more time to some threads and less time to others by setting the thread priority. There are 10 levels of Thread priorities in Java language (thread.min_PRIORITY to thread.max_priority). When two threads are in the ready state at the same time, the higher the priority is, the easier the system selects the Thread to execute. However, priority is not very reliable, because Java threads are implemented through native threads mapped to the system, so thread scheduling ultimately depends on the operating system.

State transition

Java defines five thread states, and a thread can have only one of them at any given point. Infinite waiting and waiting go together. So there are five. 1. New: a thread that has not been started after it is created. 2. Runnable: Runnable includes Running and Ready operating system thread states, where the thread may be executing or waiting for the CPU to allocate time for it to execute. After a thread object is created, other threads call the start() method of that object. Threads in this state are in the Runnable thread pool and become runnable, waiting only for CPU usage. That is, a process in the ready state has all the resources it needs to run except the CPU. Waiting: a thread in this state is not allocated CPU time and is Waiting to be explicitly woken up by another thread. For example, the object.wait() method, thread.join () method and locksupport.park () method are not set with timeout. Timed Waiting: no CPU execution time is allocated, but the system wakes up automatically after a certain amount of time without Waiting to be explicitly woken up by another thread. For example, thread.sleep (), object.wait() and thread.join() with timeout, locksupport.parknanos (), and locksupport.parkuntil () methods.

Blocked: The thread is Blocked. The difference is that a block is waiting for an exclusive lock to be acquired. This event occurs when another thread abandons the lock. Waiting is waiting for a period of time, or an awakening action to occur. The thread enters this state while waiting to enter the synchronization zone.

** blocks in one of three ways: ** (1), wait block: The running thread performs wait(), releases all resources occupied by the thread, and the JVM places the thread into a “wait pool.” Once in this state, you cannot wake up automatically. Instead, you must wait indefinitely until another thread calls notify() or notifyAll(). (2) Synchronization blocking: When a running thread acquirement a synchronization lock on an object and the lock is held by another thread, the JVM places that thread in the lock pool. (3) Other blocking: When a running thread executes a sleep() or join() method, or makes an I/O request, the JVM puts the thread in a blocked state. When the sleep() state times out, when the join() wait thread terminates or times out, or when I/O processing is complete, the thread goes back to the ready state. That is, the deadline to wait.

Note: Join () means that the current thread needs to wait for the completion of the child thread, which can be the main thread or another child thread. Join () yields the current CPU time fragment.

The thread of convergence

Essentially: Aop programming is done by calling the corresponding class’s methods through reflection. All thread frameworks eventually call the Thread constructor, and you can hook threads to print the relevant call stack information. To examine the use of threads by tripartite library functions. Convergence is about controlling the creation and use of threads in a team. Threads can be managed through a self-built thread pool.

Multiple threads concurrently obtain the task result set

1. The Futrue interface

public interface Future<V> {
    boolean cancel(boolean var1);
    boolean isCancelled(a);
    boolean isDone(a);
    V get(a) throws InterruptedException, ExecutionException;
    V get(long var1, TimeUnit var3) throws InterruptedException,ExecutionException,TimeoutException;
}

Copy the code
public class FutureDemo {

    / / the result set
    static List<Integer> list = new ArrayList<>();
    static List<Future<Integer>> futureList = new ArrayList<>();


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ExecutorService pool = Executors.newFixedThreadPool(10);
        // Submit the task
        for (int i =0; i<10; i++){ futureList.add(pool.submit(new CallableTask(i+1)));
        }
        long end = System.currentTimeMillis();

        // How do I get the result of the task?
        for (Future<Integer> future:futureList) {
            while(true) {if(future.isDone()&&! future.isCancelled()){ Integer i = future.get();// Get the result
                    System.out.println("The task I ="+i+"Fetch complete!"+new Date());
                    list.add(i);
                    break;// When the current future gets the result, jump out of the while
                }else {
                    Thread.sleep(10); / /; The rotation needs to rest for 10 seconds
                }
            }
        }
        System.out.println("list="+list);
        System.out.println("Total task time ="+(System.currentTimeMillis()-start)+", time of gathering the result ="+(System.currentTimeMillis()-end));
    }

    private static class CallableTask implements Callable<Integer> {
        int i = 0;
        public CallableTask(int i) {
            this.i = i;
        }

        @Override
        public Integer call(a) throws Exception {
            if(i==1){
                Thread.sleep(3000);// Task 1 takes 3 seconds
            }else if(i==5){
                Thread.sleep(5000);// Task 5 takes 5 seconds
            }else {
                Thread.sleep(1000);// Other tasks take 1 second
            }
            System.out.println("Task thread:"+Thread.currentThread().getName()+"The task I ="+i+"Done!");
            returni; }}}Copy the code

2.FutureTask

public class FutureTask<V> implements RunnableFuture<V> {}

public interface RunnableFuture<V> extends Runnable.Future<V> {
    void run(a); } inherits Runnable and Future, so you can start a single thread that blocks to get the result of the task. Not recommendedCopy the code

3.CompletionService

Internally, through blocking queue +FutureTask, the task can be obtained first after completion, that is, the results are sorted according to the order of completion.

4. com pletableFuture jdk1.8

The CompletableFuture satisfies the goal of concurrent execution, sequential completion of the first sequential fetch. And support for each task exception return, with streaming programming, use speed fly. JDK source support, API rich, recommended use.

Conclusion:

Futrue FutureTask CompletionService CompletableFuture
The principle of The Futrue interface The RunnableFuture interface inherits from Future+Runnable: Internally via the blocking queue +FutureTask interface JDK8 implements the Future, CompletionStage2 interface
Multi-task concurrent execution support support support support
Get the order of task results Get the results in the order they are submitted The unknown Support task sequencing Support task sequencing
Abnormal capture His capture His capture His capture Source API support, return exceptions for each task
advice CPU high-speed polling consumes resources. It can be used but is not recommended Function mismatch, concurrent tasks this piece of multiple sets of a layer, not recommended. Recommended, no JDK8CompletableFutureThe best plan before, no questions asked API extreme rich, with streaming programming, speed fly, recommended use!

The thread pool

Executors:

//newFixedThreadPool LinkedBlockingQueue Unbounded queue
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
/ / newSingleThreadExecutor, new job will create a new thread
public static ExecutorService newSingleThreadExecutor(a) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
//newCachedThreadPool Bounded queue When the current thread completes, subsequent jobs can reuse this thread
 public static newScheduledThreadPool newCachedThreadPool(a) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
//newScheduledThreadPool Indicates the periodic job or delayed task
 public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

Copy the code

ThreadPoolExecutor analysis:

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
Copy the code
  1. When the thread pool is smaller than corePoolSize, the newly submitted task creates a new thread to execute the task, even if there are idle threads in the thread pool.
  2. When the thread pool reaches corePoolSize, the new submitted task is put into the workQueue and waits for the task scheduled to execute in the thread pool.
  3. When the workQueue is full and maximumPoolSize>corePoolSize, a new thread is created to execute the newly submitted task.
  4. If the number of submitted tasks exceeds maximumPoolSize, RejectedExecutionHandler handles the new task.
  5. When there are more corePoolSize threads in the thread pool and the idle time reaches keepAliveTime, the idle thread is closed.
  6. When allowCoreThreadTimeOut(True) is set, the corePoolSize thread idle time up to keepAliveTime is also turned off.

Blocked queues in thread pools:

BlockingQueue “i.”

LinkedBlockingQueue: LinkedBlockingQueue contains two nodes, one for each element, and the atomic variable count, which starts at 0, counts the number of elements in the queue. In addition, there are two exclusive ReentrantLock locks, which control the joining and unjoining of elements in the queue. TakeLock is used to control that only one thread can acquire elements from the queue at the same time, and the other threads must wait. PutLock is used to control that only one thread can acquire the lock to add elements at the same time, and the other threads must wait. It’s essentially a production and consumption model.

ReentrantLock: ReentrantLock.

SynchronousQueue: The length of the queue is 1. Each PUT corresponds to a take method, which is suitable for the production and consumption mode.

Essential thinking:

  • LinkedBlockingQueue is bounded by the size of the queue’s elements. LinkedBlockingQueue is bounded by the size given to the queue, and is unbounded if a construction with no arguments is called. An unbounded task queue does not fail to enqueue (unless resources are exhausted).