吊打面试官之ThreadPoolExecutor深入理解

ThreadPoolExecutor
提问?
1,为什么不能使用JDK自带的Executors去创建线程池。 (阿里手册中也有这个限制)?
比如:ExecutorService executorService = Executors.newCachedThreadPool();
查看源码:

return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                              60L, TimeUnit.SECONDS,
                              new SynchronousQueue<Runnable>());

Integer.MAX_VALUE:这个会造成非常大的问题,很容易OOM。

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

Integer.MAX_VALUE:这个会造成非常大的问题,很容易OOM。

 ExecutorService executorService2 = Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

LinkedBlockingQueue(),默认也是Integer.MAX_VALUE长度,对列容易堆积到很大
的时候就会造成OOM。

ExecutorService executorService1 = Executors.newFixedThreadPool(1);
源码查看:
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

LinkedBlockingQueue(),默认也是Integer.MAX_VALUE长度,对列容易堆积到很大
的时候就会造成OOM。

如果一个线程池的线程异常了,如何处理这个异常?
1,必须要求不能影响其他线程工作。

public class ThreadPoolExecutorExceptionProcess {

    public static void main(String[] args) {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("DeviceStatusThreadPoolExecutor Thread").build();
        int queueCapacity = 2, corePoolSize = 2, maximumPoolSize = 2;
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(queueCapacity);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 10,
                TimeUnit.SECONDS, arrayBlockingQueue);

        threadPoolExecutor.execute(new MyThread(2));
        threadPoolExecutor.submit(new MyThread(2));
    }

    static class MyThread extends Thread {
        private int i;

        public MyThread(int i) {
            this.i = i;
        }

        @Override
        public void run() {
            System.out.println(i+"执行了。。。。。。");
            throw new RuntimeException("run exception");
        }
    }

}

这个代码你们心里清楚如何处理吗?

threadPoolExecutor.execute(new MyThread(2));
threadPoolExecutor.submit(new MyThread(2));

那个会抛出异常? execute还是submit。
执行execute方法
在这里插入图片描述
执行submit方法
在这里插入图片描述
异常吃了。
我们看看这个两个方法的源码

public void execute(Runnable command) 
public Future<?> submit(Runnable task)

Submit方法有一个 Future对象

执行execute断点debug
在这里插入图片描述
我们看到执行到了 afterExexute(task,thrown);这个方法。这个方法可以扩展你需要捕获的异常处理,继续debug
在这里插入图片描述
这个时候会调用uncaughtException这个方法
在这里插入图片描述
这个是一个接口的方法,ThreadGroup实现了其接口,继续debug
在这里插入图片描述
可以看到来了到这里。最终打印出来异常栈数据和信息。 UncaughtExceptionHandler解释一下这个接口。
在这里插入图片描述

就是一个线程因未捕获异常而即将终止的时候,JVM蒋使用Thread.getUncaughtExceptionHandler()获取已经设置的UncaughtExceptionHandler实例并且通过调用uncaughtException方法而传递相关的异常信息,如果一个线程没有明确的制定UncaughtExceptionHandler,就会让ThreadGroup对象作为他的handler,如果ThreadGroup对象的异常没有特殊要求,ThreadGroup就调用转发给默认的异常处理器就是上面截图看到的处理。

单个线程处理异常的方式
//单线程线程异常处理方式
Thread thread=new Thread();
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        //处理异常
    }
});

异常处理的常见方式
1,直接在我们的业务方法中直接try-catch捕获所有的异常,直接在catch块中进行异常处理。
2,线程池方式处理 继承ThreadPoolExecutor实现afterExecute方法的实现就可以了

public class ThreadPoolExecutorExtend extends ThreadPoolExecutor {
    public ThreadPoolExecutorExtend(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        //错误自定义处理
        System.out.println("自定义错误处理");
    }

}

2,设置UncaughtExceptionHandler

 public class ThreadPoolExecutorExceptionProcess {

    public static void main(String[] args) {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("DeviceStatusThreadPoolExecutor Thread").build();
        int queueCapacity = 2, corePoolSize = 2, maximumPoolSize = 2;
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(queueCapacity);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutorExtend(corePoolSize, maximumPoolSize, 10,
                TimeUnit.SECONDS, arrayBlockingQueue, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                    @Override
                    public void uncaughtException(Thread t, Throwable e) {
                        //自定义错误
                    }
                });
                return thread;
            }
        });
        //单独实例线程异常处理方式
        MyThread thread=new MyThread(2);
        threadPoolExecutor.execute(thread);

    }

    static class MyThread extends Thread {
        private int i;

        public MyThread(int i) {
            this.i = i;
//            this.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
//                @Override
//                public void uncaughtException(Thread t, Throwable e) {
//                    System.out.println("是我自己处理的异常"+t.getName()+e.getMessage());
//                }
//            });
        }

        @Override
        public void run() {
            System.out.println(i+"执行了。。。。。。");
            throw new RuntimeException("run exception");
        }
    }

}

3,自定义ThreadGroup进行错误处理

public class ThreadPoolExecutorExceptionProcess {

    static class ThreadGroupExtend extends  ThreadGroup{

        public ThreadGroupExtend(String name) {
            super(name);
        }

        public ThreadGroupExtend(ThreadGroup parent, String name) {
            super(parent, name);
        }

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            //异常业务处理
            System.out.println("业务异常处理。。。。");
        }

    }

    public static void main(String[] args) {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("DeviceStatusThreadPoolExecutor Thread").build();
        int queueCapacity = 2, corePoolSize = 2, maximumPoolSize = 2;
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(queueCapacity);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutorExtend(corePoolSize, maximumPoolSize, 10,
                TimeUnit.SECONDS, arrayBlockingQueue, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(new ThreadGroupExtend("CustomerThreadGroup"),r);
            }
        });
        //单独实例线程异常处理方式
        MyThread thread=new MyThread(2);
        threadPoolExecutor.execute(thread);

    }

    static class MyThread extends Thread {
        private int i;

        public MyThread(int i) {
            this.i = i;
//            this.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
//                @Override
//                public void uncaughtException(Thread t, Throwable e) {
//                    System.out.println("是我自己处理的异常"+t.getName()+e.getMessage());
//                }
//            });
        }

        @Override
        public void run() {
            System.out.println(i+"执行了。。。。。。");
            throw new RuntimeException("run exception");
        }
    }



}
submit方法错误处理方式
Future<?> submit = threadPoolExecutor.submit(thread);
try {
    submit.get();
} catch (Exception e) {
    //错误处理
}

源码分析
执行submit方法—> 同样会调用threadPoolExecutor的execute(ftask)

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

继续调用threadPoolExecutor的 runWorker(Worker w)
在这里插入图片描述
可以看到已经被封装成了FutureTask了,我们进入到FutureTask中去看看run方法 debug
在这里插入图片描述
看看异常,紧接着我们进入到setException里面去看看
在这里插入图片描述
在这里插入图片描述
异常数据被赋值到这对象上。
然后在调用 get方法
在这里插入图片描述
在调用 report方法
在这里插入图片描述
这个时候x就被赋值为异常对象数据。返回

总结

1,java线程池会捕获任务抛出的异常和错误,处理策略会受到我们提交任务的方式而不同。

2,submit()方式提交的任务会返给我们一个Future,如果业务方法有抛出异常,当我们调用java.util.concurrent.Future#get()方法时会抛出包装后的java.util.concurrent.ExecutionException。自己处理相应的错误
3,execute()方式提交的任务,java处理的默认策略是使用System.err.print("Exception in thread “” + t.getName() + “” ")输出日志,但是该日志不会打印到我们的日志文件中,只会在catalina.out文件中。
4,可以修改java线程池的默认处理策略,具体修改方式见上面如何处理线程异常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章