The new Thread defects

  • Each time a Thread is started, a new Thread is required to create objects and threads, resulting in poor performance. Thread pools can reuse existing threads, reducing the overhead of object creation and collection.
  • Threads lack unified management, you can create threads without limit, resulting in OOM. Thread pools control the maximum number of concurrent threads that can be created and executed.
  • Lack of advanced engineering practices such as periodic execution and thread interrupts. Thread pools provide periodic execution and concurrency control

ThreadPoolExecutor

The core variables

Parameters that need to be passed in when creating the thread pool


public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
                              0L, TimeUnit.MILLISECONDS,
                              new LinkedBlockingQueue<Runnable>(),
                              threadFactory);
}
Copy the code
  • CorePoolSize: Number of core threads, the number of threads that should be resident in the thread pool
  • MaximumPoolSize: The maximum number of threads allowed by the thread pool. Non-core threads are cleared after timeout
  • WorkQueue: blocks a queue that stores tasks waiting to be executed
  • KeepAliveTime: The length of time a thread can hold when no task is executing
  • Unit: time unit
  • ThreadFactory: threadFactory to create threads
  • RejectHandler: Policy to reject task submission (throw exception, execute task with caller’s thread, discard first task in queue to execute current task, reject task directly)

Logic for creating threads

The following tasks submitted logic from ThreadPoolExecutor. The execute method:

  1. If the number of running threads < corePoolSize, create a new thread directly, even if other threads are idle
  2. If the number of threads running >= corePoolSize 2.1 If the queue is inserted successfully, the task is committed, but no new threads are created. 2.2 If the queue is inserted successfully, the queue is full. 2.2.2 If the number of current threads >= maximumPoolSize, the specified rejection policy will be executed

Blocking queue policy

  • Submit directly. SynchronousQueue, which submits tasks directly to the thread without holding them. If there is no thread available to run the task immediately, an attempt to queue the task will fail, so a new thread will be constructed. This policy avoids locking when processing request sets that may have internal dependencies. Direct submission usually requires unbounded maximumPoolSizes to avoid rejecting newly submitted tasks.
  • Unbounded queue. Using an unbounded queue (for example, a LinkedBlockingQueue that does not have a predefined capacity) causes new tasks to wait in the queue while all corePoolSize threads are busy. In this way, the number of threads created will not exceed corePoolSize. (Therefore, the value of maximumPoolSize is invalid.) When each task is completely independent of other tasks, that is, task execution does not affect each other, it is suitable to use unbounded queue. For example, in a Web page server. This queuing can be used to handle transient burst requests, allowing the possibility for unbounded threads to grow when commands arrive continuously at an average of more than the queue can handle.
  • Bounded queues. Bounded queues (such as ArrayBlockingQueue) help prevent resource exhaustion when using limited Sizes maximumPoolQueue, but can be difficult to adjust and control. There may be a trade-off between queue size and maximum pool size: using large queues and small pools minimizes CPU utilization, operating system resources, and context switch overhead, but may result in manual throughput degradation. If tasks block frequently (for example, if they are I/O boundaries), the system may schedule time for more threads than you allow. Using small queues typically requires a larger pool size and higher CPU utilization, but may encounter unacceptable scheduling overhead, which also reduces throughput.

Execute thread logic

If a thread can be created, the addWorker method of ThreadPoolExecutor converts the Runnable we passed in to an internal Worker class inherited from AQS (new Worker(firstTask);). , in which the run method is constantly fetching tasks from the task queue to execute

The key way to

  • Execute: submits the task
  • Submit: Submits the task to obtain the execution result
  • Shutdown: Closes the thread pool until the task is complete
  • ShutdownNow: Closes the thread pool directly without waiting

Commonly used tools

Executors is a tool class that quickly creates a useful thread pool, but the ExecuteService interface returning lacks many ThreadPoolExecutor methods to watch out for

Executors.newCachedThreadPool()

CorePoolSize is 0, maximumPoolSize is the maximum integer value, keepAliveTime is 60 seconds, and queue is SynchronousQueue

Create a pool of cacheable threads, giving new tasks if there are free threads, creating new threads otherwise.

Executors.newFixedThreadPool()

CorePoolSize, maximumPoolSize custom, keepAliveTime is 0 seconds, queue is LinkedBlockingQueue

Create a fixed-length thread pool that controls the maximum number of concurrent threads, and the excess threads wait in the queue.

Executors.newScheduledThreadPool()

CorePoolSize User-defined, maximumPoolSize is the maximum integer, keepAliveTime is 0 seconds, and the queue is DelayedWorkQueue

Create a fixed – length thread pool to support scheduled and periodic task execution.

Executors.newSingleThreadExecutor()

CorePoolSize, maximumPoolSize is 1, keepAliveTime is 0 seconds, and queue is LinkedBlockingQueue

Create a single threaded thread pool that uses only one worker thread to execute tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority)

example


public class ThreadPoolTest { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { int finalI = i; executorService.execute(() -> System.out.println(finalI)); } executorService.shutdown(); }}Copy the code

The above code will print 0~9 sequentially, similar to fixed, but single will print 0~9 sequentially


public class ThreadPoolTest { public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3); // executorService.schedule(() -> System.out.println("hehe"), 1, TimeUnit.SECONDS); executorService.scheduleAtFixedRate(() -> System.out.println("hehe"), 1, 2, TimeUnit.SECONDS); // executorService.shutdown(); }}Copy the code

The above code is a typical use of newScheduledThreadPool and will execute the task as planned

Suggestions for configuring thread pools

  • Cpu-intensive tasks: Number of cpus + 1
  • IO – intensive task: Number of cpus x 2

Set the thread pool size as a reference value, and then adjust the task running status, system load, and resource utilization.

reference

Coding.imooc.com/class/195.h… And other hyperlink references

okokokokok

Recently summarized some knowledge points related to Java interview, interested friends can maintain together ~ address: github.com/xbox1994/20…