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線程池的默認處理策略,具體修改方式見上面如何處理線程異常。