Thread pool usage advice

I have written a lot of information before, but they are all organized by myself and have not been made public. If you are interested, you can look at the information I have organized in the code cloud.

However, the above information is just remembered, and it is not well categorized and sorted out. I will slowly organize it and put it in the blog when I have time. Only by constantly sorting out and sorting out can we continuously deepen memory and improve

  • thread pool naming

You can use ThreadFactoryBuilder provided by google guava

import com.google.common.util.concurrent.ThreadFactoryBuilder;

final ThreadFactory threadFactory = new ThreadFactoryBuilder()
        .setNameFormat("Orders-%d")
        .setDaemon(true)
        .build();
final ExecutorService executorService = Executors.newFixedThreadPool(10, threadFactory);

Default thread named pool-N-thread-M

N is the serial number of the thread pool (no thread pool N+1 was created)

M is the sequence number of the thread in the pool

For example pool-2-thread-3: refers to the third thread in the second thread pool in the JVM life cycle

Reference: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadFactory.html

  • Modify the thread name setName at any time

  • Safely shut down threads

Two ways:

  1. Let all threads execute shutdown()
  2. Discard thread execution and end shutdownNow() directly

For example, if we submit N tasks to the thread pool and want to return after all the tasks are executed, use shutdown()

private void sendAllEmails(List<String> emails) throws InterruptedException {
    emails.forEach(email ->
            executorService.submit(() ->
                    sendEmail(email)));
    executorService.shutdown();
    final boolean done = executorService.awaitTermination(1, TimeUnit.MINUTES);
    log.debug("All e-mails were sent so far? {}", done);
}
  • Handle interruptions carefully

A less mentioned feature of Future is cancellation

  • Monitor queue length to determine queue bounded

Improper thread pool size can slow down processing, degrade stability, and lead to memory leaks

If too few threads are configured, the queue will continue to grow and consume too much memory. And too many threads will slow down the entire system due to frequent context switches - the same thing happens in different ways. The length of the queue is critical, it must be bounded so that it can temporarily reject new requests if the thread pool is overwhelmed

final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
executorService = new ThreadPoolExecutor(n, n,
        0L, TimeUnit.MILLISECONDS,
        queue);

The above code is equivalent to Executors.newFixedThreadPool(n), but the difference is that the default implementation is an unbounded LinkedBlockingQueue. Here we are using an ArrayBlockingQueue with a fixed size of 100. That is to say, if there are already 100 tasks in the queue (and N are in execution), the new task will be rejected and a RejectedExecutionException will be thrown. Since the queue here is declared externally, we can also call its size() method from time to time to log the queue size to the log/JMX/ or whatever monitoring system you are using.

  • exception handling

  • Monitor waiting times in queues

  • synchronization queue

SynchronousQueue is a very interesting BlockingQueue. It's not even a data structure by itself. The best explanation is that it is a queue with 0 capacity. Here is a quote from the Java documentation:

Each insert operation needs to wait for a corresponding remove operation from another thread, and vice versa. There won't be any space inside the sync queue, not even a single position. You can't peek a synchronized queue, because an element exists only when you're removing it; you can't insert an element into it if no other thread is trying to remove it; you can't Do a traversal because it has nothing. . .

Synchronized queues are similar to the rendezvous channels used in CSP and Ada.

What does it have to do with thread pools? You can try to use SynchronousQueue in ThreadPoolExecutor:

BlockingQueue<Runnable> queue = new SynchronousQueue<>();
ExecutorService executorService = new ThreadPoolExecutor(n, n,
        0L, TimeUnit.MILLISECONDS,
        queue);

We created a thread pool with two threads, and a SynchronousQueue. Since SynchronousQueue is essentially a queue with a capacity of 0, this ExecutorService can only accept new tasks when there are idle threads. If all threads are busy, the new task will be rejected immediately without waiting. This can be very useful in background execution scenarios that are either executed immediately or discarded immediately.

Either execute it immediately or discard it immediately

Guess you like

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