监控线程池中的线程并且捕获出现异常的线程

目录

监控线程池的运行状态

代码实例

1.自定义ThreadPoolExecutor

2.自定义Runnable

3.启动类

4.在执行任务之前为每个线程增加异常拦截器

线程池中的一个线程出现异常


监控线程池的运行状态

需要关注ThreadPoolExecutor中的四个方法

  • Shutdown():启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务。
  • ShutdownNow():尝试停止所有主动执行的任务,停止等待任务的处理,并返回正在等待执行的任务列表。
  • beforeExecutor(Thread t, Runnable r):在给定的线程中执行给定的Runnable之前调用方法
  • afterExecutor(Runnable r, Throwable t):完成指定Runnable的执行后调用方法。

代码实例

  • 监控shutdown时任务的执行情况以及执行时间

1.自定义ThreadPoolExecutor

package com.nieyp.customThreadPoolExecutor;

import com.nieyp.existException.ObjectUncaughtExceptionHandler;

import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutorUtil extends ThreadPoolExecutor {

    private ConcurrentHashMap<String, Date> startTimes;
    private String poolName;

    public ExecutorUtil(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, String poolName) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new ExecutorUtil.EventThreadFactory(poolName));
        this.poolName = poolName;
        this.startTimes = new ConcurrentHashMap<>();
    }

    //线程池关闭的统计:已经执行的任务,正在执行的任务,为执行的任务
    @Override
    public void shutdown() {
        System.out.println(String.format(this.poolName + "Going to shutDown. Executed tasks: %d" + "Running tasks: %d, Pending tasks: %d ",
                this.getCompletedTaskCount(),
                this.getActiveCount(),
                this.getQueue().size()));

        System.out.println(String.format(this.poolName + "Going to shutDown. Executed tasks: %d" + "Running tasks: %d, Pending tasks: %d ",
                this.getCompletedTaskCount(),
                this.getActiveCount(),
                this.getQueue().size()));

        System.out.println(String.format(this.poolName + "Going to shutDown. Executed tasks: %d" + "Running tasks: %d, Pending tasks: %d ",
                this.getCompletedTaskCount(),
                this.getActiveCount(),
                this.getQueue().size()));
        if (this.getQueue().size() > 5){
            System.out.println("阻塞队列的任务数量大于5,需要重新设置线程池的参数");
        }
        super.shutdown();
    }

    @Override
    public List<Runnable> shutdownNow() {
        System.out.println(String.format(this.poolName + "Going to shutdownNow. Executed tasks: %d" + "Running tasks: %d, Pending tasks: %d ",
                this.getCompletedTaskCount(),
                this.getActiveCount(),
                this.getQueue().size()));
        return super.shutdownNow();
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        startTimes.put(String.valueOf(r.hashCode()), new Date());
        
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        Date startTime = startTimes.remove(String.valueOf(r.hashCode()));
        Date endTime = new Date();
        long diff = startTime.getTime() - endTime.getTime();
        System.out.println(String.format("task running time: %d", diff));*/
    }

    public static ExecutorService newFixedThreadPool(int nThreads, String poolName){
        return new ExecutorUtil(nThreads, nThreads, 0L, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<>(), poolName);
    }

    //自定义线程工厂,一般默认即可
    static class EventThreadFactory implements ThreadFactory{
        private final ThreadGroup group;
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private static final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        EventThreadFactory(String poolName) {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
            namePrefix = poolName + "-pool" + poolNumber.getAndIncrement() + "-thread";
        }

        @Override
        public Thread newThread(Runnable runnable) {
            Thread thread = new Thread(group, runnable, namePrefix + threadNumber.getAndIncrement());
            if (thread.isDaemon()) {
                thread.setDaemon(false);
            }
            if (thread.getPriority() != Thread.NORM_PRIORITY) {
                thread.setPriority(Thread.NORM_PRIORITY);
            }
            return thread;
        }
    }
}

2.自定义Runnable

package com.nieyp.customThreadPoolExecutor;

public class Reading implements Runnable{
    private String name;
    private int count;

    public Reading(String name, int count) {
        this.name = name;
        this.count = count;
    }

    @Override
    public void run() {
        while (count > 0){
            System.out.println(Thread.currentThread().getName() + "reading" + name);
            count--;
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
    
}

 3.启动类

public class Main {
    public static void main(String[] args) {

        Reading runnable01 = new Reading("Java编程思想", 3);
        Reading runnable02 = new Reading("Spring实战", 3);
        Reading runnable03 = new Reading("SpringBoot实战", 3);
        Reading runnable04 = new Reading("Mysql权威指南", 3);
        Reading runnable05 = new Reading("SpringCloud 实战", 3);
        ExecutorService service = ExecutorUtil.newFixedThreadPool(10, "imooc-nieyp");
        service.execute(runnable01);
        service.execute(runnable02);
        service.execute(runnable03);
        service.execute(runnable04);
        service.execute(runnable05);
    }
}

 4.在执行任务之前为每个线程增加异常拦截器

  • 定义异常拦截器
public class ObjectUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {
        System.out.println("threadName:" + thread.currentThread().getName() + "出错,错误信息为:" + throwable.getMessage());
    }
}
  • 重写ExecutorUtil中的beforeExecute,手动设置异常情况
public class Reading implements Runnable{
    private String name;
    private int count;

    public Reading(String name, int count) {
        this.name = name;
        this.count = count;
    }

    @Override
    public void run() {
        //如果任务的name为Java编程思想则会抛出异常
        if (name.equals("Java编程思想")){
            throw new RuntimeException();
        }
        while (count > 0){
            System.out.println(Thread.currentThread().getName() + "reading" + name);
            count--;
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

}



@Override
    protected void beforeExecute(Thread t, Runnable r) {
          t.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());
    }
  • 打印的信息

imooc-nieyp-pool1-thread3readingSpringBoot实战
imooc-nieyp-pool1-thread3readingSpringBoot实战
imooc-nieyp-pool1-thread3readingSpringBoot实战
imooc-nieyp-pool1-thread2readingSpring实战
imooc-nieyp-pool1-thread2readingSpring实战
imooc-nieyp-pool1-thread2readingSpring实战
imooc-nieyp-pool1-thread5readingSpringCloud 实战
imooc-nieyp-pool1-thread5readingSpringCloud 实战
imooc-nieyp-pool1-thread5readingSpringCloud 实战
imooc-nieyp-pool1-thread4readingMysql权威指南
imooc-nieyp-pool1-thread4readingMysql权威指南
imooc-nieyp-pool1-thread4readingMysql权威指南
threadName:imooc-nieyp-pool1-thread1出错,错误信息为:
java.lang.RuntimeException
    at com.nieyp.customThreadPoolExecutor.Reading.run(Reading.java:15)

//当前在beforeExecute方法中我们也可以根据Runnable中的name进行判断是否增加异常拦截器,因为线程池中的线程都是复用的,所以只给单个线程设置实际上没有什么作用,只是模拟这个点

@Override
protected void beforeExecute(Thread t, Runnable r) {
    Reading r1 = (Reading) r;
    if (r1.getName().equals("Java编程思想")){
        t.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());
    }
}

线程池中的一个线程出现异常

1.抛异常出来并打印在控制台上
    1.1,众所周知submit底层其实也是调用的execute,因此它也有异常只是处理方法不一样,它们的区别              如下
            1.1.1,execute没有返回值。可以执行任务,但无法判断任务是否成功完成。——实现Runnable接口
            1.1.2,submit返回一个future。可以用这个future来判断任务是否成功完成。——实现Callable接口
    1.2,执行方式为execute:抛出异常显示在控制台
    1.3,执行方式为submit:submit提交时异常被存储在线程结果信息中,当调用get方法是判断线程运行结果状态,
        有异常就抛出存储的异常信息,因此submit运行异常我们只能用get方法来拿到
        
2.其他线程任务不受影响

3.异常线程会被回收
    3.1先删掉线程又再调用创建线程的方法,所以异常线程不是被回收,而是被删除了再创建一个新的顶替了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值