Java Multithreading--2, ThreadPoolExecutor Detailed Explanation (1)

      ThreadPoolExecutor, as the core class of multi-threaded development, is also a must-have question for interviews in major factories. As a Javaer, it is necessary to understand its operation principle in detail.

      The source code of the construction method of ThreadPoolExecutor (students who do not understand English go out and turn right):

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param corePoolSize the number of threads to keep in the pool, even
 *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
 * @param maximumPoolSize the maximum number of threads to allow in the
 *        pool
 * @param keepAliveTime when the number of threads is greater than
 *        the core, this is the maximum time that excess idle threads
 *        will wait for new tasks before terminating.
 * @param unit the time unit for the {@code keepAliveTime} argument
 * @param workQueue the queue to use for holding tasks before they are
 *        executed.  This queue will hold only the {@code Runnable}
 *        tasks submitted by the {@code execute} method.
 * @param threadFactory the factory to use when the executor
 *        creates a new thread
 * @param handler the handler to use when execution is blocked
 *        because the thread bounds and queue capacities are reached
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code threadFactory} or {@code handler} is null
 */
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;
}

   The meaning of several important parameters must be mastered. This article will not describe them one by one. The following mainly introduces how to create threads through ThreadPoolExecutor.

      First, let's take a look at some of the more important global variables and methods:

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; }

       ThreadPoolExecutor saves some information of the current thread pool through the ctl variable. ctl is an atomic integer variable, which ensures its unique atomicity. It actually saves a 32-bit binary number (on the win platform, The following is only based on 32 bits), and the saved information is as follows:

     Several important global variables are as follows:

     2 constants:

  1. COUNT_BITS: The length of the integer variable is reduced by 3, which is used for shifting, which ensures that the first 3 bits of the number are used to identify the state.
  2. CAPACITY: capacity, used to save the number of threads in the current thread pool, is a number whose first 3 digits are 0 and all 29 digits after it are 1 (0001 1111 1111 1111 1111 1111 1111 1111). In this way, when other numbers are ANDed with him, the first 3 digits are guaranteed to be 0, and the last 29 digits retain the original number.

     5 state constants: RUNING < SHUTDOWN < STOP < TIDYING < TERMINATED, by shifting 29 bits to the left and filling the empty bits with 0, a status flag of the first 3 bits is guaranteed.

    3 common methods:

  1. runStateOf: The parameter is usually the value of the current ctl (obtained by ctl.get()), and then let it do the & operation with ~CAPACITY (1110 0000 0000 0000 0000 0000 0000 0000), the result is the first 3 bits retain the original value, and then 29 bits are 0, and the status information is obtained.
  2. workerCountOf: Same as above, but doing & operation with CAPACITY (0001 1111 1111 1111 1111 1111 1111 1111), the result is that the first 3 bits are 0, the last 29 bits are the original value, and the number of threads is obtained.
  3. ctlOf: The parameters are runState (the last 29 bits are 0), workerCount (the first 3 bits are 0), after the OR operation, the first 3 bits keep the value of runState, and the last 29 bits keep the value of workerCount, which is the current thread pool all status information.

    In subsequent operations, the above variables and methods will appear frequently, please bear in mind. 

    Next: Analysis of the execute method of ThreadPoolExecutor:

    https://my.oschina.net/u/3760321/blog/1615188

Guess you like

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