首先,這個問題考察的是你對執行緒池 execute 方法和 submit 方法的理解,在 Java 執行緒池的使用中,我們可以透過 execute 方法或 submit 方法給執行緒池新增任務,但如果執行緒池中的程式在執行時,遇到了未處理的異常會怎麼呢?接下來我們一起來看。
1.execute方法
execute 方法用於提交一個不需要返回值的任務給執行緒池執行,它接收一個 Runnable 型別的引數,並且不返回任何結果。
它的使用示例程式碼如下:
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 方法提交任務 executor.execute(new Runnable() { @Override public void run() { System.out.println("Task running in " + Thread.currentThread().getName()); try { // 模擬任務執行 Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("Task was interrupted"); } System.out.println("Task finished"); } }); // 關閉執行緒池 executor.shutdown(); } }
2.submit方法
submit 方法用於提交一個需要返回值的任務(Callable 物件),或者不需要返回值但希望獲取任務狀態的任務(Runnable 物件,但會返回一個 Future 物件)。
它接收一個 Callable 或 Runnable 型別的引數,並返回一個 Future 物件,透過該物件可以獲取任務的執行結果或檢查任務的狀態。
2.1 提交Callable任務
示例程式碼如下:
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) { // 建立一個固定大小的執行緒池 ExecutorService executorService = Executors.newFixedThreadPool(2); // 提交一個 Callable 任務給執行緒池執行 Future<String> future = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(2000); // 模擬任務執行時間 return "Task's execution result"; } }); try { // 獲取任務的執行結果 String result = future.get(); System.out.println("Task result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } // 關閉執行緒池 executorService.shutdown(); } }
2.2 提交Runnable任務
提交 Runnable 任務並獲取 Future 物件,示例程式碼如下:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class SubmitRunnableDemo { public static void main(String[] args) { // 建立一個固定大小的執行緒池 ExecutorService executorService = Executors.newFixedThreadPool(2); // 提交一個 Runnable 任務給執行緒池執行,並獲取一個 Future 物件 Future<?> future = executorService.submit(new Runnable() { @Override public void run() { System.out.println("Task is running in thread: " + Thread.currentThread().getName()); } }); // 檢查任務是否完成(這裏只是爲了示例,實際使用中可能不需要這樣做) if (future.isDone()) { System.out.println("Task is done"); } else { System.out.println("Task is not done yet"); } // 關閉執行緒池 executorService.shutdown(); } }
3.遇到未處理異常
執行緒池遇到未處理的異常執行行為和新增任務的方法有關,也就是說 execute 方法和 submit 方法在遇到未處理的異常時執行行為是不一樣的。
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)); // 新增任務一 executor.execute(() -> { String tName = Thread.currentThread().getName(); System.out.println("執行緒名:" + tName); throw new RuntimeException("丟擲異常"); }); // 新增任務二 executor.execute(() -> { String tName = Thread.currentThread().getName(); System.out.println("執行緒名:" + tName); throw new RuntimeException("丟擲異常"); }); } }
以上程式的執行結果如下:
從上述結果可以看出,執行緒池中的核心和最大執行緒數都為 1 的情況下,到遇到未處理的異常時,執行任務的執行緒卻不一樣,這說明了:當使用 execute 方法時,如果遇到未處理的異常,會丟擲未捕獲的異常,並將當前執行緒進行銷燬。
3.2 submit方法遇到未處理異常
然而,當我們將執行緒池的新增任務方法換成 submit() 之後,執行結果又完全不同了,以下是示例程式碼:
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)); // 新增任務一 Future<?> future = executor.submit(() -> { String tName = Thread.currentThread().getName(); System.out.println("執行緒名:" + tName); throw new RuntimeException("丟擲異常"); }); // 新增任務二 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()); } } }
以上程式的執行結果如下:
從上述結果可以看出,submit 方法遇到未處理的異常時,並將該異常封裝在 Future 的 get 方法中,而不會直接影響執行任務的執行緒,這樣執行緒就可以繼續複用了。
小結
執行緒池在遇到未處理的異常時,不同新增任務的方法的執行行為是不同的:
execute 方法:遇到未處理的異常,執行緒會崩潰,並列印異常資訊。
submit 方法:遇到未處理的異常,執行緒本身不會受到影響(執行緒可以複用),只是將異常資訊封裝到返回的物件 Future 中。