นากกายปล่อยเส้นด้ายเรืองแสงหลายเส้นจากกระสวยเข้าเลนงานแยกบนโต๊ะมิ้นต์

ExecutorService: ส่งงาน แล้วปิดพูลให้สะอาด

น้องกายEngineering

ถ้าเคย new Thread(...).start() ทีละงานจนเครื่องสะอึก ปัญหาไม่ได้อยู่ที่จาวาช้าอย่างเดียว แต่อยู่ที่เราไม่ได้จำกัดและปิดทรัพยากรให้ครบ

ExecutorService คือตัวรับงานมาคิวแล้วให้เธรดในพูลรัน แนวทางคลาสสิกคือ Executors.newFixedThreadPool(n) ส่วนบน JDK 21+ มี Executors.newVirtualThreadPerTaskExecutor() ที่แต่ละงานได้ virtual thread ใหม่ เหมาะงาน I/O พร้อมกันสูง ตามเอกสาร Virtual Threads

ตัวอย่างด้านล่างใช้พูลคงที่ที่รันได้บน JDK กว้างกว่า และโชว์การ shutdown + awaitTermination ให้จบสะอาด

เลือกแบบไหน

  • Fixed / cached platform pool: งานคำนวณหรือต้องการเพดานเธรดชัด
  • Virtual-thread-per-task (JDK 21+): งานบล็อก I/O จำนวนมาก โดยไม่ต้องตั้งขนาดพูลเอง
  • อย่าลืมปิดพูล: ไม่งานจะค้างใน JVM ตอนเทสต์หรือตอนปิดแอป
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Main {
  public static void main(String[] args) throws Exception {
    ExecutorService exec = Executors.newFixedThreadPool(2);
    try {
      Future<String> f = exec.submit(() -> {
        Thread.sleep(20);
        return "done";
      });
      String result = f.get(2, TimeUnit.SECONDS);
      if (!"done".equals(result)) {
        throw new IllegalStateException("unexpected: " + result);
      }
      System.out.println(result);
    } finally {
      exec.shutdown();
      if (!exec.awaitTermination(2, TimeUnit.SECONDS)) {
        exec.shutdownNow();
      }
    }
  }
}

Recap

  1. ส่งงานผ่าน ExecutorService แทนการสร้างเธรดมือทุกครั้ง
  2. เลือก fixed pool เมื่อต้องการเพดานชัด และพิจารณา virtual-thread-per-task เมื่อขึ้น JDK 21+ กับงาน I/O
  3. ปิดพูลด้วย shutdown / shutdownNow ทุกครั้งหลังจบ
  4. ใส่ timeout ให้ Future.get เพื่อไม่ค้างเงียบ