学习笔记:线程池执行抛出异常处理

1.使用线程池抛出异常处理:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>
package com.tomdd.wechatarticle;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.junit.jupiter.api.Test;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * <h1>线程池中抛出异常测试</h1>
 * 线程池中线程频繁出现未捕获异常
 * 那线程的复用率就大大降低了,需要不断地创建新线程。
 *
 * @author zx
 * @date 2022年10月17日 11:20
 */
public class ThreadExecutorTest {

    /**
     * ·corePool:核心线程池的大小。
     * ·maximumPool:最大线程池的大小。
     * ·BlockingQueue:用来暂时保存任务的工作队列。
     * ·RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满),execute()方法将要调用的Handler。
     */
    //private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
    //        new ArrayBlockingQueue<>(200), new NameTreadFactory(), new MyIgnorePolicy());

    //new ThreadFactoryBuilder()  guava 工具类提供的。
    //private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
    //        new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d").build());


    //要捕获那些没被业务代码捕获的异常,可以设置Thread类的uncaughtExceptionHandler属性。
    // 这时使用ThreadFactoryBuilder会比较方便,ThreadFactoryBuilder是guava提供的ThreadFactory生成器。
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d")
            .setUncaughtExceptionHandler((t, e) -> System.out.println(t.getName() + "发生异常" + e.getCause()))
            .build());


    /**
     * 线程池中的线程执行任务抛出异常没有被捕获
     * 在不断地创建新线程
     */
    @Test
    public void test01() {
        IntStream.rangeClosed(1, 5).forEach(i -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            threadPoolExecutor.execute(() -> {
                int j = 1 / 0;
            });
        });
    }

    /**
     * 线程池中执行任务的线程如果抛出异常没有被捕获
     * 设置Thread类的uncaughtExceptionHandler属性,线程池中原有的线程没有复用
     * 通过UncaughtExceptionHandler的设置,只是做了一层异常的保底处理。
     */
    @Test
    public void test02() {
        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;
            });
        });
    }


    /**
     * 线程池中执行任务的线程如果抛出异常没有被捕获
     * 设置Thread类的uncaughtExceptionHandler属性,并没有实现复用效果
     * <p>
     * 把excute改成submit试试:
     * 通过submit提交线程可以屏蔽线程中产生的异常,达到线程复用。当get()执行结果时异常才会抛出
     * 通过submit提交的线程,当发生异常时,会将异常保存,待future.get();时才会抛出
     *
     * 总结:
     * 1、线程池中线程中异常尽量手动捕获
     *
     * 2、通过设置ThreadFactory的UncaughtExceptionHandler可以对未捕获的异常做保底处理,通过execute提交任务,线程依然会中断,而通过submit提交任务,可以获取线程执行结果,线程异常会在get执行结果时抛出。
     */
    @Test
    public void test03() {
        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();
            }
        });
    }

    /**
     * 线程池中的线程执行任务抛出异常被捕获
     * 线程池中的额线程可以被重复使用
     */
    @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());
                }
            });
        });
    }


    //设置线程名称
    static class NameTreadFactory implements ThreadFactory {

        private final AtomicInteger mThreadNum = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
            System.out.println(t.getName() + " has been created");
            return t;
        }
    }

    //拒绝策略: 当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满)
    static class MyIgnorePolicy implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            doLog(r, e);
        }

        private void doLog(Runnable r, ThreadPoolExecutor e) {
            System.err.println(r.toString() + " rejected");
            System.out.println("completedTaskCount: " + e.getCompletedTaskCount());
        }
    }


}

2.总结:

  1. 线程池中线程中异常尽量手动捕获
  2. 通过设置ThreadFactoryUncaughtExceptionHandler可以对未捕获的异常做保底处理,通过execute提交任务,线程依然会中断,而通过submit提交任务,可以获取线程执行结果,线程异常会在get执行结果时抛出
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值