Thread pool principle (based on jdk1.8.0_20)

effect

  1. Reduce resource consumption. Reduce the cost of thread creation and destruction by reusing already created threads
  2. Improve responsiveness. When the task arrives, the task can be executed immediately without waiting until the thread is created
  3. Improve thread manageability. Threads are scarce resources. If they are created without restrictions, it will not only consume system resources, but also reduce the stability of the system. Using thread pools can be used for unified allocation, tuning and monitoring.

use

Through the Executors class, four thread pools are provided

Way Features
newCachedThreadPool Cacheable thread pool, the length of the thread pool exceeds the processing needs, the thread can be recycled, and the thread pool is infinite. When the second task is executed, the first task has been completed, and the thread of the first task will be reused instead of rebuild
newFixedThreadPool Fixed-length thread pool, which can control the maximum number of concurrent threads, and the excess threads will wait in the queue
newScheduledThreadPool Fixed-length thread pool, supports timing and periodic task execution
newSingleThreadExecutor A singleton thread pool that executes tasks with a unique worker thread to ensure that all tasks are executed in the specified order (FIFO or LIFO)
public class Task extends Thread{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class TestCachedThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            Task task = new Task();
            executorService.execute(task);
        }
        //pool-1-thread-1 is running
        //pool-1-thread-5 is running
        //pool-1-thread-2 is running
        //pool-1-thread-4 is running
        //pool-1-thread-3 is running
        //必须显式结束,不然程序永远不会结束
        executorService.shutdown();
    }
}

It seems that the thread pool is not used. In fact, because there are no reusable threads, new threads are always created.

public class TestFixedThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 5; i++) {
            Task task = new Task();
            executorService.execute(task);
        }
        //pool-1-thread-1 is running
        //pool-1-thread-2 is running
        //pool-1-thread-1 is running
        //pool-1-thread-2 is running
        //pool-1-thread-1 is running
        executorService.shutdown();
    }
}
public class TestScheduledThreadPool {

    public static void main(String[] args) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
        //任务,第1次任务延迟的时间,2次任务间隔时间,时间单位
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("task 1 " + System.currentTimeMillis());
            }
        }, 1, 5, TimeUnit.SECONDS);
        //两者互不影响
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("task 2 " + System.currentTimeMillis());
            }
        }, 1, 2,TimeUnit.SECONDS);
        //task 1 1521949174111
        //task 2 1521949174112
        //task 2 1521949176106
        //task 2 1521949178122
        //task 1 1521949179104
        //task 2 1521949180114
    }
}
public class TestSingleThreadExecutor {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            Task task = new Task();
            executorService.execute(task);
        }
        //pool-1-thread-1 is running
        //pool-1-thread-1 is running
        //pool-1-thread-1 is running
        //pool-1-thread-1 is running
        //pool-1-thread-1 is running
        executorService.shutdown();
    }
}

source code

write picture description here

condition

condition meaning value
RUNNING Accept new tasks and process incoming tasks -536870912
SHUTDOWN Do not accept new tasks, process tasks entering the queue 0
STOP Do not accept new tasks, do not process tasks that enter the queue, and interrupt ongoing tasks 536870912
TIDYING All tasks are executed and the workerCount is 0. The thread goes to the state TIDYING and executes the terminated() hook method 1073741824
TERMINATED terminated() has been executed 1610612736

write picture description here

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

The ctl of type AtomicInteger represents the control state of ThreadPoolExecutor, which is a composite type variable. It wraps two concepts with the help of high and low bits
. 1. runState is the running state of the thread pool, occupying the upper three bits of ctrl.
2. workerCount is the number of currently active threads in the thread pool. , occupy the lower 29 bits of ctl

COUNT_BITS represents the number of bits occupied by workerCount, that is, 29, and CAPACITY represents the maximum number of active threads in the thread pool theory, that is, 536870911
write picture description here
0 is represented by 32 0s at the bottom of Java, no matter how many bits left, or 32 0s, That is, the value of SHUTDOWN is 0, and the value of TIDYING is 010 for the upper three bits and 0 for the lower 29.

private static int runStateOf(int c)     { return c & ~CAPACITY; }

~ means bitwise inversion, CAPACITY represents 3 high-order 0s and 29 low-order 1s, while ~CAPACITY represents 3 high-order 1s, 2 low-order 9 0s, and then joins the input parameter c Perform a bitwise AND operation, that is, the upper 3 bits remain as they are, and the lower 29 bits are all set to 0, which also obtains the running state of the thread pool, runState.

private static int ctlOf(int rs, int wc) { return rs | wc; }

The incoming rs represents the running state of the thread pool, runState, which is an int with a value in the upper 3 bits and all 0s in the lower 29 bits, and wc represents the number of valid threads in the thread pool workerCount, whose upper 3 bits are all 0, And the lower 29 bits have value int, the runState and workerCount are bitwise ORed, that is, the number filled with the upper 3 bits of runState and the lower 29 bits of workerCount

Constructor

ThreadPoolExecutor has a total of 4 constructors, but the following constructors are finally called

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.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
parameter meaning
corePoolSize Core thread pool size
maximumPoolSize The maximum size of the thread pool
keepAliveTime The time that threads other than core threads survive when the thread pool is idle
TimeUnit Unit of thread activity retention time
BlockingQueue<Runnable> Task queue, a blocking queue for holding tasks waiting to be executed
ThreadFactory Factory for setting up threads
RejectedExecutionHandler saturation strategy

Here is a brief talk about corePoolSize and maximumPoolSize. The following analogy can be used to learn, corePoolSize=basic personnel of the company, maximumPoolSize=basic personnel of the company + outsourced personnel. corePoolSize, the minimum number of worker threads to keep alive, when it is smaller than corePoolSize, a new thread will be started directly to process the task, regardless of whether there are idle threads in the thread pool

RejectedExecutionHandler is an interface with 4 implementation classes corresponding to 4 processing strategies. These 4 implementation classes are static inner classes of ThreadPoolExecutor

kind Strategy
AbortPolicy Abandon the task, throw a runtime exception
CallerRunsPolicy perform tasks
DiscardPolicy Ignore, nothing will happen
DiscardOldestPolicy kicks the task that was first queued (last executed) from the queue

execute execution process

  1. First, the thread pool determines whether the basic thread pool is full? Not full, create a worker thread to execute the task. If it is full, go to the next process.
  2. Secondly, the thread pool determines whether the work queue is full? If it is not full, the newly submitted task is stored in the work queue. If it is full, go to the next process.
  3. Finally, the thread pool determines whether the entire thread pool is full (that is, whether the number of threads is less than the maximum capacity of the thread pool)? If it is not full, a new worker thread is created to execute the task, and when it is full, it is handed over to the saturation strategy to process the task.

write picture description here

retry use
break retry to jump to retry and no longer enter the loop
continue retry jump to retry and enter the loop again

public static void main(String[] args) {
    breakRetry();
    continueRetry();
}

private static void breakRetry() {
    int i = 0;
    retry:
    for (; ; ) {
        System.out.println("start");
        for (; ; ) {
            i++;
            if (i == 4)
                break retry;
        }
    }
    //start 进入外层循环
    //4
    System.out.println(i);
}

private static void continueRetry() {
    int i = 0;
    retry:
    for(;;) {
        System.out.println("start");
        for(;;) {
            i++;
            if (i == 3)
                continue retry;
            System.out.println("end");
            if (i == 4)
                break retry;
        }
    }
    //start 第一次进入外层循环
    //end i=1输出
    //end i=2输出
    //start 再次进入外层循环
    //end i=4输出
    //4 最后输出
    System.out.println(i);
}

The connection and difference between submit and execute

The execute method is defined in the Executor interface and implemented directly by ThreadPoolExecutor

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

Reference blog

Analysis and use of Java thread pool
[1] http://ifeve.com/java-threadpool/
[2] https://www.jianshu.com/p/ade771d2c9c0
[3] https://blog.csdn.net /lipeng_bigdata/article/details/51232266
[4] https://blog.csdn.net/qq_19431333/article/details/59030892
Analysis of the usage of four Java thread pools
[5] https://www.cnblogs.com/ruiati/ p/6134131.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325419015&siteId=291194637