“This is the 16th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

Did you notice during development that when we use thread pools we rarely handle errors that occur during execution, and is it okay not to handle errors? Will not processing cause the thread pool to end? How do we handle errors if we need to handle them? So today from the following aspects

1. The thread pool is abnormal

Three cases of abnormal thread pools are demonstrated in code.

1.1Runable Execution Exception (Service Exception)

Test code:

public class ThreadPoolExceptionTest {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(2.new ThreadFactory() {
            AtomicInteger integer = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "mxsm-"+integer.getAndIncrement()); }}); executorService.execute(() -> { System.out.println(1);
            int i = 1/0;
            System.out.println(i);
        });
        executorService.execute(() -> {
            for(;;) {try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName()+"Current time:"+System.currentTimeMillis());
                } catch(InterruptedException e) { e.printStackTrace(); }}}); System.out.println("Main thread execution completed"); }}Copy the code

Run the program to observe the test results:

Conclusion: The thread pool is fine, Runable’s exception does not cause the thread pool to stop running, other threads are fine

Thread that failed to execute Runable will be destroyed and a new thread will be created to keep pool 2 fixed

1.2 Failed to submit a Task to the Task queue

Test code:

public class ThreadPoolExceptionTest {

    public static void main(String[] args) {

        ExecutorService executorService = new ThreadPoolExecutor(1.1.100, TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),new ThreadFactory(){
            AtomicInteger integer = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "mxsm-"+integer.getAndIncrement()); }});for(int i = 0; i < 3; ++i){
            final int b = i;
            executorService.execute(() -> {
                for(;;) {try {
                        TimeUnit.SECONDS.sleep(1);
                        System.out.println(Thread.currentThread().getName()+ b +"Current time:"+System.currentTimeMillis());
                    } catch(InterruptedException e) { e.printStackTrace(); }}}); } System.out.println("End of main thread"); }}Copy the code

Run the program to observe the test results:

Conclusion: When the thread pool uses the default reject policy, the thread pool will directly throw an error when the thread pool submits a task to a task queue that is full, which will affect the main thread’s subsequent running.

The scope and manner of submitting a task to a full task queue are determined by the rejection policy

1.3 The thread pool is abnormal

The thread pool exceptions mentioned here include, but are not limited to, the possibility of constantly creating new threads while setting the thread pool size, resulting in threads consuming all of the server’s resources

Test code:

/ * * *@author mxsm
 * @date2022/2/1 even *@Since1.0.0 * * Sets the memory size * -xmx2m * -xMS2m * */
public class ThreadPoolExceptionTest {


    public static void main(String[] args) {

        ExecutorService executorService = Executors.newCachedThreadPool();
        final AtomicInteger integer = new AtomicInteger();
        for(int i = 0; i <=100000; ++i){
            final  int b = i;
            executorService.submit(new Runnable() {
                @Override
                public void run(a) {
                    try {
                        System.out.println(integer.getAndIncrement());
                        TimeUnit.SECONDS.sleep(b);
                    } catch(InterruptedException e) { e.printStackTrace(); }}}); } System.out.println("End of main thread"); }}Copy the code

Run the program to observe the test results:

Conclusion: Some exceptions caused by the thread pool can cause the thread pool to exit directly, which can cause problems or exit of the host thread or the main application.

Tips: One of the many problems demonstrated here

2. How to handle exceptions

Thread pool tasks are performed primarily through ThreadPoolExecutor#runWork:

  1. If the task Runnable executes incorrectly the thread pool will throw out an error to exit the while loop
  2. Handles Worker exit logic

  1. Delete the Worker executing the error from the workers collection.

Tips: If you keep executing Runnable errors, the thread labels in the thread pool will get bigger and bigger.

Finally, the thread executing the current Runnable terminates. The thread pool and other threads are not affected.

2.1 Handling method of Runable Execution Exception (Service Exception

Because Runable execution exception does not affect the entire system, there is no real system error exit due to task execution in the thread pool. So there are usually two ways to handle exceptions for thread pool tasks:

  • (You can also print logs)
  • Catch an exception without throwing it out

2.2 Handling the Fault that a Task is Submitted to the Task Queue Is Full

This exception depends on which rejection policy is used. There are four built-in Rejection policies in Java:

  • CallerRunsPolicy: After a rejected task is added, the thread of the current thread pool is called to execute the rejected task
  • AbortPolicy: Directly throws an exception
  • DiscardPolicy: Discards tasks rejected by the thread pool without throwing exceptions or executing them.
  • DiscardOldestPolicy: When a task is rejected, the system discards the oldest task in the task queue and adds the new task to the queue.
  • To customize the policy, implement the RejectedExecutionHandler interface

For the exception that the task is submitted to the task queue is full, if the catch is not carried out, the operation of the whole system will not be affected, and if it may lead to system interruption. You need to handle errors.

2.3 Exceptions caused by the thread pool itself

An exception caused by the thread pool itself may cause a program to break if the program must rely on the thread pool to perform its function, and when the thread pool itself causes an exception as demonstrated above. It doesn’t matter whether you handle an exception or not. The whole program crashed! Thread pools, however, are just an alternative to catch and handle possible exceptions. (But it doesn’t completely prevent crashes.)

3. How to use thread pools from exceptions

  1. It is important to set the maximum number of threads in the thread pool to prevent the incessant creation of thread pools from consuming all server resources
  2. The blocking queue of the thread pool should not be set to unbounded queue, because the same incessant adding of tasks may consume the server resources
  3. Catch and handle foreseeable exceptions as much as possible