線程池遇到未處理的異常會(huì)崩潰嗎?
首先,這個(gè)問(wèn)題考察的是你對(duì)線程池 execute 方法和 submit 方法的理解,在 Java 線程池的使用中,我們可以通過(guò) execute 方法或 submit 方法給線程池添加任務(wù),但如果線程池中的程序在執(zhí)行時(shí),遇到了未處理的異常會(huì)怎么呢?接下來(lái)我們一起來(lái)看。
1.execute方法
execute 方法用于提交一個(gè)不需要返回值的任務(wù)給線程池執(zhí)行,它接收一個(gè) Runnable 類型的參數(shù),并且不返回任何結(jié)果。
它的使用示例代碼如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecuteDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
// 使用 execute 方法提交任務(wù)
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println("Task running in " + Thread.currentThread().getName());
try {
// 模擬任務(wù)執(zhí)行
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Task was interrupted");
}
System.out.println("Task finished");
}
});
// 關(guān)閉線程池
executor.shutdown();
}
}
2.submit方法
submit 方法用于提交一個(gè)需要返回值的任務(wù)(Callable 對(duì)象),或者不需要返回值但希望獲取任務(wù)狀態(tài)的任務(wù)(Runnable 對(duì)象,但會(huì)返回一個(gè) Future 對(duì)象)。
它接收一個(gè) Callable 或 Runnable 類型的參數(shù),并返回一個(gè) Future 對(duì)象,通過(guò)該對(duì)象可以獲取任務(wù)的執(zhí)行結(jié)果或檢查任務(wù)的狀態(tài)。
2.1 提交Callable任務(wù)
示例代碼如下:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class SubmitCallableDemo {
public static void main(String[] args) {
// 創(chuàng)建一個(gè)固定大小的線程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 提交一個(gè) Callable 任務(wù)給線程池執(zhí)行
Future<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(2000); // 模擬任務(wù)執(zhí)行時(shí)間
return "Task's execution result";
}
});
try {
// 獲取任務(wù)的執(zhí)行結(jié)果
String result = future.get();
System.out.println("Task result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// 關(guān)閉線程池
executorService.shutdown();
}
}
2.2 提交Runnable任務(wù)
提交 Runnable 任務(wù)并獲取 Future 對(duì)象,示例代碼如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class SubmitRunnableDemo {
public static void main(String[] args) {
// 創(chuàng)建一個(gè)固定大小的線程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 提交一個(gè) Runnable 任務(wù)給線程池執(zhí)行,并獲取一個(gè) Future 對(duì)象
Future<?> future = executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("Task is running in thread: " + Thread.currentThread().getName());
}
});
// 檢查任務(wù)是否完成(這里只是為了示例,實(shí)際使用中可能不需要這樣做)
if (future.isDone()) {
System.out.println("Task is done");
} else {
System.out.println("Task is not done yet");
}
// 關(guān)閉線程池
executorService.shutdown();
}
}
3.遇到未處理異常
線程池遇到未處理的異常執(zhí)行行為和添加任務(wù)的方法有關(guān),也就是說(shuō) execute 方法和 submit 方法在遇到未處理的異常時(shí)執(zhí)行行為是不一樣的。
3.1 execute方法遇到未處理異常
示例代碼如下:
import java.util.concurrent.*;
public class ThreadPoolExecutorExceptionTest {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(100));
// 添加任務(wù)一
executor.execute(() -> {
String tName = Thread.currentThread().getName();
System.out.println("線程名:" + tName);
throw new RuntimeException("拋出異常");
});
// 添加任務(wù)二
executor.execute(() -> {
String tName = Thread.currentThread().getName();
System.out.println("線程名:" + tName);
throw new RuntimeException("拋出異常");
});
}
}
以上程序的執(zhí)行結(jié)果如下:
圖片
從上述結(jié)果可以看出,線程池中的核心和最大線程數(shù)都為 1 的情況下,到遇到未處理的異常時(shí),執(zhí)行任務(wù)的線程卻不一樣,這說(shuō)明了:當(dāng)使用 execute 方法時(shí),如果遇到未處理的異常,會(huì)拋出未捕獲的異常,并將當(dāng)前線程進(jìn)行銷毀。
3.2 submit方法遇到未處理異常
然而,當(dāng)我們將線程池的添加任務(wù)方法換成 submit() 之后,執(zhí)行結(jié)果又完全不同了,以下是示例代碼:
import java.util.concurrent.*;
public class ThreadPoolExecutorExceptionTest {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(100));
// 添加任務(wù)一
Future<?> future = executor.submit(() -> {
String tName = Thread.currentThread().getName();
System.out.println("線程名:" + tName);
throw new RuntimeException("拋出異常");
});
// 添加任務(wù)二
Future<?> future2 =executor.submit(() -> {
String tName = Thread.currentThread().getName();
System.out.println("線程名:" + tName);
throw new RuntimeException("拋出異常");
});
try {
future.get();
} catch (Exception e) {
System.out.println("遇到異常:"+e.getMessage());
}
try {
future2.get();
} catch (Exception e) {
System.out.println("遇到異常:"+e.getMessage());
}
}
}
以上程序的執(zhí)行結(jié)果如下:
圖片
從上述結(jié)果可以看出,submit 方法遇到未處理的異常時(shí),并將該異常封裝在 Future 的 get 方法中,而不會(huì)直接影響執(zhí)行任務(wù)的線程,這樣線程就可以繼續(xù)復(fù)用了。
小結(jié)
線程池在遇到未處理的異常時(shí),不同添加任務(wù)的方法的執(zhí)行行為是不同的:
- execute 方法:遇到未處理的異常,線程會(huì)崩潰,并打印異常信息。
- submit 方法:遇到未處理的異常,線程本身不會(huì)受到影響(線程可以復(fù)用),只是將異常信息封裝到返回的對(duì)象 Future 中。