关于java:面试官线程池执行过程中遇到异常会发生什么怎样处理

27次阅读

共计 6995 个字符,预计需要花费 18 分钟才能阅读完成。

线程遇到未解决的异样就完结了

这个好了解,当线程呈现未捕捉异样的时候就执行不上来了,留给它的就是垃圾回收了。

线程池中线程频繁呈现未捕捉异样

当线程池中线程频繁呈现未捕捉的异样,那线程的复用率就大大降低了,须要一直地创立新线程。

做个试验:

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;});});
 }
}

新建一个只有一个线程的线程池,每隔 0.1s 提交一个工作,工作中是一个 1 / 0 的计算。

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)

可见每次执行的线程都不一样,之前的线程都没有复用。起因是因为呈现了未捕捉的异样。

咱们把异样捕捉试试:

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

可见当异样捕捉了,线程就能够复用了。

问题来了,咱们的代码中异样不可能全副捕捉

如果要捕捉那些没被业务代码捕捉的异样,能够设置 Thread 类的 uncaughtExceptionHandler 属性。这时应用 ThreadFactoryBuilder 会比拟不便,ThreadFactoryBuilder是 guava 提供的 ThreadFactory 生成器。

new ThreadFactoryBuilder()
.setNameFormat("customThread %d")
.setUncaughtExceptionHandler((t, e) -> System.out.println(t.getName() + "产生异样" + e.getCause()))
.build()

批改之后:

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 捕捉到:" + t.getName() + "产生异样" + 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.currentThread().getName() + "执行");
    int j = 1 / 0;
   });
  });
 }
}
线程 customThread 0 执行
UncaughtExceptionHandler 捕捉到:customThread 0 产生异样 / by zero
线程 customThread 1 执行
UncaughtExceptionHandler 捕捉到:customThread 1 产生异样 / by zero
线程 customThread 2 执行
UncaughtExceptionHandler 捕捉到:customThread 2 产生异样 / by zero
线程 customThread 3 执行
UncaughtExceptionHandler 捕捉到:customThread 3 产生异样 / by zero
线程 customThread 4 执行
UncaughtExceptionHandler 捕捉到:customThread 4 产生异样 / by zero

可见,后果并不是咱们设想的那样,线程池中原有的线程没有复用!所以通过 UncaughtExceptionHandler 想将异样吞掉使线程复用这招貌似行不通。它只是做了一层异样的保底解决。

将 excute 改成 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 捕捉到:" + t.getName() + "产生异样" + 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.currentThread().getName() + "执行");
    int j = 1 / 0;
   });
   try {future.get();
   } catch (InterruptedException e) {e.printStackTrace();
   } catch (ExecutionException e) {e.printStackTrace();
   }
  });
 }
}
线程 customThread 0 执行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
线程 customThread 0 执行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
线程 customThread 0 执行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
线程 customThread 0 执行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
线程 customThread 0 执行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero

通过 submit 提交线程能够屏蔽线程中产生的异样,达到线程复用。当 get()执行后果时异样才会抛出。

起因是通过 submit 提交的线程,当产生异样时,会将异样保留,待 future.get(); 时才会抛出。

这是 Futuretask 的局部 run() 办法,看 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();}
}

将异样存在 outcome 对象中,没有抛出,再看 get 办法:

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);
}

当 outcome 是异样时才抛出。

总结

1、线程池中线程中异样尽量手动捕捉

2、通过设置 ThreadFactory 的 UncaughtExceptionHandler 能够对未捕捉的异样做保底解决,通过 execute 提交工作,线程仍然会中断,而通过 submit 提交工作,能够获取线程执行后果,线程异样会在 get 执行后果时抛出。

本文链接:https://blog.csdn.net/weixin_…

版权申明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协定,转载请附上原文出处链接和本申明。

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿(2021 最新版)

2. 别在再满屏的 if/ else 了,试试策略模式,真香!!

3. 卧槽!Java 中的 xx ≠ null 是什么新语法?

4.Spring Boot 2.5 重磅公布,光明模式太炸了!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0