The thread ends when it encounters an unhandled exception
It's easy to understand. When an uncapped exception occurs in a thread, it can't be executed. What's left is garbage collection.
Uncapped exceptions frequently occur in threads in the thread pool
When uncapped exceptions occur frequently in threads in the thread pool, the reuse rate of threads is greatly reduced, and new threads need to be created constantly.
Do an experiment:
public class ThreadExecutor { private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d").build()); @Test public void test() { IntStream.rangeClosed(1, 5).forEach(i -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } threadPoolExecutor.execute(() -> { int j = 1/0; });}); } }
Create a new thread pool with only one thread and submit a task every 0.1s. In the task, there is a 1 / 0 calculation.
Exception in thread "customThread 0" java.lang.ArithmeticException: / by zero at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:748) Exception in thread "customThread 1" java.lang.ArithmeticException: / by zero at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:748) Exception in thread "customThread 2" java.lang.ArithmeticException: / by zero at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:748) Exception in thread "customThread 3" java.lang.ArithmeticException: / by zero at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:748) Exception in thread "customThread 4" java.lang.ArithmeticException: / by zero at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:748) Exception in thread "customThread 5" java.lang.ArithmeticException: / by zero at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:748)
It can be seen that the threads executed each time are different, and the previous threads are not reused. The reason is because an uncapped exception occurred.
Let's try to catch exceptions:
public class ThreadExecutor { private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d").build()); @Test public void test() { IntStream.rangeClosed(1, 5).forEach(i -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } threadPoolExecutor.execute(() -> { try { int j = 1 / 0; } catch (Exception e) { System.out.println(Thread.currentThread().getName() +" "+ e.getMessage()); } }); }); } }
customThread 0 / by zero customThread 0 / by zero customThread 0 / by zero customThread 0 / by zero customThread 0 / by zero
It can be seen that when the exception is caught, the thread can be reused.
The problem is that it is impossible to catch all the exceptions in our code
If you want to catch exceptions that are not caught by the business code, you can set the uncaughtExceptionHandler property of the Thread class. At this time, it is more convenient to use ThreadFactoryBuilder, which is the ThreadFactory builder provided by guava.
new ThreadFactoryBuilder() .setNameFormat("customThread %d") .setUncaughtExceptionHandler((t, e) -> System.out.println(t.getName() + "exception occurred" + e.getCause())) .build()
After modification:
public class ThreadExecutor { private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder() .setNameFormat("customThread %d") .setUncaughtExceptionHandler((t, e) -> System.out.println("UncaughtExceptionHandler Captured:" + t.getName() + "exception occurred" + e.getMessage())) .build()); @Test public void test() { IntStream.rangeClosed(1, 5).forEach(i -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } threadPoolExecutor.execute(() -> { System.out.println("thread " + Thread.currentThread().getName() + "implement"); int j = 1 / 0; }); }); } }
thread customThread 0 implement UncaughtExceptionHandler Captured: customThread 0 exception occurred/ by zero thread customThread 1 implement UncaughtExceptionHandler Captured: customThread 1 exception occurred/ by zero thread customThread 2 implement UncaughtExceptionHandler Captured: customThread 2 exception occurred/ by zero thread customThread 3 implement UncaughtExceptionHandler Captured: customThread 3 exception occurred/ by zero thread customThread 4 implement UncaughtExceptionHandler Captured: customThread 4 exception occurred/ by zero
It can be seen that the result is not what we think. The original threads in the thread pool are not reused! Therefore, it seems impossible to swallow exceptions and reuse threads through UncaughtExceptionHandler. It just does a layer of exception protection.
Try changing excute to submit
public class ThreadExecutor { private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder() .setNameFormat("customThread %d") .setUncaughtExceptionHandler((t, e) -> System.out.println("UncaughtExceptionHandler Captured:" + t.getName() + "exception occurred" + e.getMessage())) .build()); @Test public void test() { IntStream.rangeClosed(1, 5).forEach(i -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } Future<?> future = threadPoolExecutor.submit(() -> { System.out.println("thread " + Thread.currentThread().getName() + "implement"); int j = 1 / 0; }); try { future.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); } }
thread customThread 0 implement java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero thread customThread 0 implement java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero thread customThread 0 implement java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero thread customThread 0 implement java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero thread customThread 0 implement java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
The submit thread can shield the exceptions generated in the thread and achieve thread reuse. An exception is thrown only when get() executes the result.
The reason is that the thread submitted through submit will save the exception when an exception occurs and wait for future.get(); Will be thrown when.
This is part of the run() method of Futuretask. See setException:
public void run() { try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } } protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); } }
Save the exception in the outcome object without throwing it. See the get method again:
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); } private V report(int s) throws ExecutionException { Object x = outcome; if (s == NORMAL) return (V)x; if (s >= CANCELLED) throw new CancellationException(); throw new ExecutionException((Throwable)x); }
Thrown when the outcome is an exception.
summary
1. Try to catch exceptions in the thread pool manually
2. By setting the UncaughtExceptionHandler of ThreadFactory, you can guarantee the minimum handling of uncaught exceptions. If you submit the task through execute, the thread will still be interrupted. If you submit the task through submit, you can obtain the thread execution result, and the thread exception will be thrown when you get the execution result.
Link to this article: https://blog.csdn.net/weixin_37968613/article/details/108407774
Copyright notice: This article is the original article of the blogger and follows the CC 4.0 BY-SA copyright agreement. For reprint, please attach the source link of the original text and this notice.
Recent hot article recommendations:
1.1000 + Java interview questions and answers (2021 latest version)
2.Stop playing if/ else on the full screen. Try the strategy mode. It's really fragrant!!
3.what the fuck! What is the new syntax of xx ≠ null in Java?
4.Spring Boot 2.5 heavy release, dark mode is too explosive!
5.Java development manual (Songshan version) is the latest release. Download it quickly!
Feel good, don't forget to like + forward!