Explanation of the meaning of java thread pool keepAliveTime

  • 2021-08-21 20:22:08
  • OfStack

Before, the attribute in the thread pool: keepAliveTime is vague, and after reading it, it will be forgotten after 1 period of time, so record 1 here.

The interpretation in jdk of keepAliveTime is:

When the number of threads is larger than the core, this is the maximum amount of time that the extra idle threads wait for a new task before termination.

It makes people feel vague, Summary 1 roughly means: For example, the maximum number of threads in the thread pool is 50, and only 40 threads are running, which is equivalent to 10 idle threads. These 10 idle threads can't keep him open straight, because the existence of threads will be particularly good for resources, so it is necessary to set the survival time of one idle thread, so the explanation should be very clear.

In this way, if you forget it, you will come and have a look at OK.

Supplement: Thread pool status and KeepAliveTime parameters

5 states


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

Cyclic getTask method


/**
  * Performs blocking or timed wait for a task, depending on
  * current configuration settings, or returns null if this worker
  * must exit because of any of:
  * 1. There are more than maximumPoolSize workers (due to
  * a call to setMaximumPoolSize).
  * 2. The pool is stopped.
  * 3. The pool is shutdown and the queue is empty.
  * 4. This worker timed out waiting for a task, and timed-out
  * workers are subject to termination (that is,
  * {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
  * both before and after the timed wait.
  *
  * @return task, or null if the worker must exit, in which case
  *   workerCount is decremented
  */
 private Runnable getTask() {
  boolean timedOut = false; // Did the last poll() time out?
  retry:
  for (;;) {
   int c = ctl.get();
   int rs = runStateOf(c);
   // Check if queue empty only if necessary.
   if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    decrementWorkerCount();
    return null;
   }
   boolean timed;  // Are workers subject to culling?
   for (;;) {
    int wc = workerCountOf(c);
    timed = allowCoreThreadTimeOut || wc > corePoolSize;
    // Default allowCoreThreadTimeOut For false Unless the program specifies 
    // ( 1 ) When the core thread is not exceeded, the default allowCoreThreadTimeOut For false Hour 
    //timed Value is false , always break Drop, the thread will not be destroyed 
    // ( 2 ) When the number of core threads is exceeded, the default allowCoreThreadTimeOut For false Hour 
    //timed Value is true If the maximum value is exceeded, it is destroyed; If timeout If it passes, it will be destroyed 
    //  If allowCoreThreadTimeOut For true , then timed Always be true
    if (wc <= maximumPoolSize && ! (timedOut && timed))
     break;
    if (compareAndDecrementWorkerCount(c))
     return null;
    c = ctl.get(); // Re-read ctl
    if (runStateOf(c) != rs)
     continue retry;
    // else CAS failed due to workerCount change; retry inner loop
   }
   try {
    Runnable r = timed ?
     workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
     workQueue.take();
    if (r != null)
     return r;
    timedOut = true;
   } catch (InterruptedException retry) {
    timedOut = false;
   }
  }
 }

Two cases where the thread pool state is greater than the SHUTDOWN value

1. Call the shutdown method

When the thread pool calls the shutdown method, The state of the thread pool is first set to SHUTDOWN, Then all threads in the thread pool are traversed and the interrupt method is called once. If the threads in hibernation will be activated, the activated threads and the threads calling the shutdown method itself will try to call the tryTerminate method. This method will determine that if the number of threads recorded in the thread pool is 0, the thread state will be changed to TERMINATED, and this value is 3, which will be greater than the SHUTDOWN state value.

2. Call the shutdownNow method

When the thread calls the shutdownNow method, first modify the state of the thread to STOP, which is greater than the value of SHUTDOWN, and then it will activate the thread through interruption, but it comes more violently, without locking and some basic judgments, and interrupt directly; Before calling tryTerminate, all the elements in the blocking queue are emptied and assembled into an List list as the return value of the shutdownNow method. In other words, tasks that are not executed are available in the return value of shutdownNow after execution. In some necessary cases of the program, you can make some state decisions on threads through isTerminating, isTerminated, isStopped and isShutdown of the thread pool.

KeepAliveTime Parameters


workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)

When there are no tasks in the blocking queue, it will wake up automatically when the waiting time reaches keepAliveTime milliseconds, instead of falling asleep forever.

keepAliveTime, if it is through newCachedThreadPool, the default is 1 minute timeout. If the aforementioned instantaneous impact is encountered, the number of thread pools will expand rapidly instantaneously, and the life cycle of these instantaneous expanded threads will be at least 1 minute.

If this parameter is set, when timeout, return null will jump out of the loop and reclaim the thread.


if (wc <= maximumPoolSize && ! (timedOut && timed))
     break;
    if (compareAndDecrementWorkerCount(c))
     return null;

allowCoreThreadTimeout: The core thread does not exit by default, but you can set this parameter to true to get the core thread to exit as well.

The default Executors factory only has newCachedThreadPool, and timeout is 60 seconds. In case of timeout, and the number of threads exceeds the number of core threads, the threads will be destroyed. Maintain the corePoolSize number (corePoolSize is 0 if it is cached).


 /**
  * Timeout in nanoseconds for idle threads waiting for work.
  * Threads use this timeout when there are more than corePoolSize
  * present or if allowCoreThreadTimeOut. Otherwise they wait
  * forever for new work.
  */
 private volatile long keepAliveTime;
 /**
  * If false (default), core threads stay alive even when idle.
  * If true, core threads use keepAliveTime to time out waiting
  * for work.
  */
 private volatile boolean allowCoreThreadTimeOut;

The minimum thread pool is corePoolSize and the maximum is maximumPoolSize. Unless allowCoreThreadTimeOut and timeout are set, the number of threads may be reduced to 0 in this case, and the maximum is Integer.MAX_VALUE.

Core pool size is the minimum number of workers to keep alive(and not allow to time out etc) unless allowCoreThreadTimeOut is set, in which case the minimum is zero.


/**
  * Creates a thread pool that creates new threads as needed, but
  * will reuse previously constructed threads when they are
  * available. These pools will typically improve the performance
  * of programs that execute many short-lived asynchronous tasks.
  * Calls to <tt>execute</tt> will reuse previously constructed
  * threads if available. If no existing thread is available, a new
  * thread will be created and added to the pool. Threads that have
  * not been used for sixty seconds are terminated and removed from
  * the cache. Thus, a pool that remains idle for long enough will
  * not consume any resources. Note that pools with similar
  * properties but different details (for example, timeout parameters)
  * may be created using {@link ThreadPoolExecutor} constructors.
  *
  * @return the newly created thread pool
  */
 public static ExecutorService newCachedThreadPool() {
  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
          60L, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>());
 }
 /**
  * Creates a thread pool that creates new threads as needed, but
  * will reuse previously constructed threads when they are
  * available, and uses the provided
  * ThreadFactory to create new threads when needed.
  * @param threadFactory the factory to use when creating new threads
  * @return the newly created thread pool
  * @throws NullPointerException if threadFactory is null
  */
 public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
          60L, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(),
          threadFactory);
 }

If the timeout timeout is set to 0, it means not waiting


public E poll(long timeout, TimeUnit unit) throws InterruptedException {
  return pollFirst(timeout, unit);
 }

The details are as follows


public E pollFirst(long timeout, TimeUnit unit)
  throws InterruptedException {
  long nanos = unit.toNanos(timeout);
  final ReentrantLock lock = this.lock;
  lock.lockInterruptibly();
  try {
   E x;
   while ( (x = unlinkFirst()) == null) {
    if (nanos <= 0)
     return null;
    nanos = notEmpty.awaitNanos(nanos);
   }
   return x;
  } finally {
   lock.unlock();
  }
 }

Related articles: