线程池异常你都了解如何处理吗?

大家在开发的过程中是否发现,我们使用线程池的时候很少去处理运行过程中出现的错误,不处理错误这样没关系吗?不处理会不会导致线程池结束?如果需要处理错误我们应该如何进行处理呢?那么今天从以下几个方面来看一下


线程池异常你都了解如何处理吗?


1.线程池异常

通过代码来演示三种异常线程池的情况。

1.1Runable执行异常(业务异常)

测试代码:

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()+" 当前时间:"+System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println("主线程执行完成");
    }
}

运行程序观察测试结果:

线程池异常你都了解如何处理吗?

结论:线程池正常运行,Runable的异常不会导致线程池停止运行,其他的线程正常运行

Tips: 执行Runable发生错误的线程将会被销毁会重新建一个线程,以保证固定线程池2的数量

1.2 提交任务到任务队列已满异常

测试代码:

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 +" 当前时间:"+System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        System.out.println("主线程结束");
    }
}

运行程序观察测试结果:

线程池异常你都了解如何处理吗?

结论:线程池使用默认的拒绝策略的时候,当线程池提交任务到任务队列已满线程池会直接抛出错误,进而影响到主线程的后续的运行如果没有在主线程中进行错误处理(没有打印主线程结束)

Tips: 提交任务到任务队列已满异常影响的范围和方式由拒绝策略决定

1.3 线程池本身异常

这里说的线程池本身异常包括但不仅限于在设置线程池大小的时候,可能不停的新建线程导致线程消耗完成了服务器的所有资源

测试代码:

/**
 * @author mxsm
 * @date 2022/2/1 22:49
 * @Since 1.0.0
 *
 * 设置内存大小
 * -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() {
                    try {
                        System.out.println(integer.getAndIncrement());
                        TimeUnit.SECONDS.sleep(b);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        System.out.println("主线程结束");
    }
}

运行程序观察测试结果:

线程池异常你都了解如何处理吗?

结论:线程池导致某些异常会导致线程池直接退出可能同时导致主线程或者主应用发生问题或者退出。

Tips: 这里演示的众多问题中的一个

2. 异常如何处理

线程池执行任务主要是通过 ThreadPoolExecutor#runWork 执行的:

线程池异常你都了解如何处理吗?

  1. 如果任务Runnable执行错误线程池就会往外抛错误退出while循环
  2. 处理Worker退出逻辑
线程池异常你都了解如何处理吗?

  1. 从workers集合中删除执行报错的Worker.

Tips: 如果你不停的执行Runnable错误的话,线程池的线程标号会越来越大,也就是这里这个原因。

最终执行当前Runnable线程结束。并不会影响线程池和其他线程。

2.1 Runable执行异常(业务异常)处理方式

由于Runable执行异常并不会影响到整个系统的运行,不会因为在线程池中执行任务报错导致真系统错误退出。所以线程池执行任务的异常处理方式通常有两种:

2.2 提交任务到任务队列已满异常处理

这个异常取决于使用的何种拒绝策略。Java内置的拒绝策略有四种:

对于提交任务到任务队列已满异常,如果不进行catch不影响整个整个系统的运行可以不进行处理,如果可能会导致系统中断。就需要对错误进行处理。

2.3 线程池本身导致的异常

线程池本身导致的异常可能会导致程序的中断,如果程序必须依靠线程池才能完成对应功能,当线程池本身导致异常如上面演示的。那么是否处理异常都无关紧要。整个程序直接崩溃!但是线程池只是一个备选方案,可以将可能的异常进行捕获处理。(但是不能完全杜绝程序崩溃的问题)

3. 从异常看如何使用线程池

  1. 线程池一定要设置最大线程数,防止不停地创建线程池消耗掉服务器所有的资源
  2. 线程池的阻塞队列尽量不要设置为无界队列,原因:同样不停地添加任务可能把服务器的资源消耗完成
  3. 对于可预见的异常尽量进行捕获处理

我是蚂蚁背大象,文章对你有帮助点赞关注我,文章有不正确的地方请您斧正留言评论~谢谢

展开阅读全文

页面更新:2024-04-25

标签:线程   异常   队列   主线   错误   策略   结束   代码   测试   程序

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top