พิจารณาโปรแกรมง่ายๆ:
public static void main(String[] args) throws Exception {
// Create an ExecutorService with a fixed number of threads: three
ExecutorService service = Executors.newFixedThreadPool(3);
// Pass a simple Runnable task to the ExecutorService
service.submit(() -> System.out.println("done"));
}
การรันโปรแกรมสร้างเอาต์พุตคอนโซลที่เราคาดหวัง:
แต่ไม่ได้ตามด้วยผลลัพธ์ที่เรามักจะเห็นใน IntelliJ IDEA:
เรามักจะเห็นว่าเมื่อโปรแกรมสิ้นสุดลง
ทำไมถึงเกิดขึ้น?
คำอธิบายของ เมธอด newFixedThreadPool()บอกเราว่าเธรดที่สร้างโดยใช้ExecutorServiceยังคงอยู่จนกว่าจะหยุดทำงานอย่างชัดเจน นั่นหมายถึงเพราะเราส่งต่องานไปยังExecutorServiceเธรดจึงถูกสร้างขึ้นเพื่อเรียกใช้งาน และเธรดนั้นจะยังคงอยู่แม้ว่างานจะเสร็จสิ้นแล้วก็ตาม
หยุดที่ ExecutorService
เป็นผลให้เราต้อง "ปิด" (หรือหยุด) ExecutorService เราสามารถทำได้สองวิธี:
-
void shutdown() — หลังจากเรียกใช้เมธอดนี้ ExecutorService จะหยุดรับงานใหม่ งานทั้งหมดที่ส่งไปยังExecutorService ก่อนหน้านี้ จะยังคงทำงานต่อไป
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(3); service.submit(() -> System.out.println("task 1")); service.submit(() -> System.out.println("task 2")); service.shutdown(); // A RejectedExecutionException will occur here service.submit(() -> System.out.println("task 3")); }
-
List<Runnable> shutdownNow() — เมธอดนี้พยายามหยุดงานที่กำลังทำงานอยู่ งานที่ยังคงรอ ถึงตาของพวกเขาจะถูกยกเลิกและส่งคืนเป็นรายการRunnables
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(5); List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i))); List<Runnable> runnables = service.shutdownNow(); runnables.forEach(System.out::println); }
เอาท์พุต:
2
4
3
java.util.concurrent.FutureTask@1e80bfe8[ยังไม่เสร็จ งาน = java.util.concurrent.Executors$RunnableAdapter@4edde6e5[งานที่รวมไว้ = Test$$Lambda$16/0x0000000800b95040@70177ecd]]
java.util.concurrent .FutureTask@cc34f4d[ยังไม่เสร็จสมบูรณ์ งาน = java.util.concurrent.Executors$RunnableAdapter@66a29884[งานที่รวมไว้ = Test$$Lambda$16/0x0000000800b95040@4769b07b]]
java.util.concurrent.FutureTask@6f539caf[ยังไม่เสร็จ งาน = java.util.concurrent.Executors$RunnableAdapter@17a7cec2[งานที่รวมไว้ = Test$$Lambda$16/0x0000000800b95040@65b3120a]]
5
ดำเนินการเสร็จสิ้นด้วยรหัสออก 0
ผลลัพธ์จะแตกต่างกันไปในแต่ละรัน เอาต์พุตมี 2 บรรทัด:
-
ตัวเลขหมายความว่าExecutorServiceจัดการเพื่อประมวลผลงานที่เกี่ยวข้อง โดยแสดงตัวเลขจากรายการที่เราใช้สร้างงาน
-
ผลลัพธ์ของการเรียก ใช้เมธอด toString()บนวัตถุFutureTask อ็อบเจ็กต์เหล่านี้เป็นงานที่ส่งไปยัง ExecutorService แต่ไม่ได้รับการประมวลผล
ผลลัพธ์มีความแตกต่างที่น่าสนใจอีกอย่างหนึ่ง ในโลกอุดมคติ อันดับแรกเราจะเห็นตัวเลขที่แสดงทั้งหมด ตามด้วยวัตถุFutureTask แต่ปัญหาการซิงโครไนซ์ทำให้บรรทัดในเอาต์พุตสับสน
วิธีการอื่นๆ
ExecutorServiceมีหลายวิธีที่เกี่ยวข้องกับการหยุด:
-
บูลีน waitTermination (การหมดเวลานาน, หน่วย TimeUnit) — เมธอดนี้บล็อกเธรดที่เรียกใช้ การบล็อกจะสิ้นสุดลงทันทีที่มีเหตุการณ์ใดเหตุการณ์หนึ่งในสามเหตุการณ์ต่อไปนี้เกิดขึ้น:
- หลังจากเรียกเมธอดshutdown() งานที่ใช้งานอยู่และงานตามกำหนดเวลาทั้งหมดจะถูกดำเนินการ
- การหมดเวลาที่กำหนดโดยพารามิเตอร์วิธีการผ่านไปแล้ว
- เธรดที่เรียกใช้ เมธอด waitTermination()ถูกยกเลิก
เมธอดจะคืนค่าจริงหากExecutorServiceหยุดทำงานก่อนหมดเวลา และเป็นเท็จหากหมดเวลาไปแล้ว
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(2); service.submit(() -> System.out.println("task 1")); service.submit(() -> System.out.println("task 2")); service.submit(() -> System.out.println("task 3")); service.shutdown(); System.out.println(service.awaitTermination(1, TimeUnit.MICROSECONDS)); }
-
บูลีน isShutdown() — คืนค่าจริงหากเมธอดshutdown ()หรือshutdownNow()ถูกเรียกใช้บนExecutorService
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(2); service.submit(() -> System.out.println("task 1")); service.submit(() -> System.out.println("task 2")); service.submit(() -> System.out.println("task 3")); System.out.println(service.isShutdown()); service.shutdown(); System.out.println(service.isShutdown()); }
-
บูลีน isTerminated() — คืนค่าจริงหาก เมธอด shutdown()หรือshutdownNow()ถูกเรียกใช้บนExecutorServiceและงานทั้งหมดเสร็จสิ้น
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(5); List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i))); service.shutdownNow(); System.out.println(service.isTerminated()); }
ตัวอย่างโค้ดที่ใช้วิธีการเหล่านี้:
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(16);
Callable<String> task = () -> {
Thread.sleep(1);
return "Done";
};
// Add 10,000 tasks to the queue
List<Future<String>> futures = IntStream.range(0, 10_000)
.mapToObj(i -> service.submit(task))
.collect(Collectors.toList());
System.out.printf("%d tasks were submitted for execution.%n", futures.size());
// Attempt to shut down
service.shutdown();
// Wait 100 milliseconds to finish the work
if (service.awaitTermination(100, TimeUnit.MILLISECONDS)) {
System.out.println("All tasks completed!");
} else {
// Stop forcibly
List<Runnable> notExecuted = service.shutdownNow();
System.out.printf("%d tasks were not started.%n", notExecuted.size());
}
System.out.printf("Total tasks completed: %d.%n", futures.stream().filter(Future::isDone).count());
}
เอาท์พุต (แตกต่างจากรันทูรัน):
ไม่ได้เริ่มงาน 9170 งาน
งานทั้งหมดที่เสร็จสมบูรณ์: 830 งาน
กระบวนการเสร็จสิ้นด้วยรหัสออก 0
GO TO FULL VERSION