ExecutorService และชนิดพูลเธรด
สร้างพูลเธรดแบบคงที่ แบบแคช แบบเธรดเดียว และแบบตั้งเวลา พร้อมทำความเข้าใจเวลาที่ควรใช้แต่ละแบบ
ExecutorService และชนิดพูลเธรด เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงใช้กลุ่มเธรด
การสร้าง Thread ใหม่สำหรับทุกงานมีค่าใช้จ่ายสูง เพราะการสร้างและทำลายเธรดมีค่าใช้จ่ายเพิ่มเติม กลุ่มเธรด จะนำเธรดจำนวนคงที่กลับมาใช้เพื่อทำงานหลายงาน ช่วยลดค่าใช้จ่ายและควบคุมการใช้ทรัพยากร
import java.util.concurrent.*;
// Bad practice: new thread per task
new Thread(() -> System.out.println("task")).start();
// Better: reuse threads from a pool
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> System.out.println("task from pool"));
pool.shutdown();newFixedThreadPool
สร้างกลุ่มที่มีเธรดจำนวน N พอดี งานเพิ่มเติมจะเข้าคิวจนกว่าจะมีเธรดพร้อมใช้งาน เหมาะเมื่อทราบระดับการทำงานแบบขนานที่ต้องการ
ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
final int taskId = i;
pool.submit(() -> {
System.out.println("Task " + taskId + " on " + Thread.currentThread().getName());
});
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);newCachedThreadPool
สร้างเธรดตามต้องการและนำเธรดที่ไม่ได้ใช้งานกลับมาใช้ใหม่ เธรดที่ว่างนาน 60 วินาทีจะถูกยุติ เหมาะกับงานจำนวนมากที่ทำงานช่วงสั้น ๆ แต่มีความเสี่ยงสำหรับงานที่ทำงานนานหรือมีจำนวนไม่จำกัด เพราะอาจใช้หน่วยความจำจนหมด
ExecutorService cached = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
cached.submit(() -> {
// quick task
double result = Math.sqrt(Math.random());
});
}
cached.shutdown();newSingleThreadExecutor
กลุ่มที่มีเธรดเพียงหนึ่งเธรด งานจะทำงานตามลำดับการส่งเข้ามา เหมาะสำหรับควบคุมการเข้าถึงทรัพยากรที่ใช้ร่วมกันให้เป็นลำดับเดียว
ExecutorService single = Executors.newSingleThreadExecutor();
single.submit(() -> System.out.println("Task 1"));
single.submit(() -> System.out.println("Task 2"));
single.submit(() -> System.out.println("Task 3"));
// Always prints: Task 1, Task 2, Task 3 (sequential)
single.shutdown();newScheduledThreadPool
รองรับงานที่กำหนดเวลาและงานที่ทำซ้ำ ใช้สำหรับการตรวจสอบเป็นระยะ การลองใหม่ และการบำรุงรักษาเบื้องหลัง
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// One-shot delay
scheduler.schedule(() -> System.out.println("Delayed!"),
3, TimeUnit.SECONDS);
// Fixed rate (every 2 seconds)
scheduler.scheduleAtFixedRate(
() -> System.out.println("Tick"),
0, 2, TimeUnit.SECONDS);
// Run later: scheduler.shutdown();VirtualThreadExecutor (Java 21 ขึ้นไป)
Java 21 เพิ่มเธรดเสมือน ซึ่งมีน้ำหนักเบาและสร้างได้ในต้นทุนต่ำ ใช้ Executors.newVirtualThreadPerTaskExecutor() สำหรับงานที่ใช้การรับส่งข้อมูลเป็นหลัก
// Java 21+
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
final int id = i;
exec.submit(() -> {
Thread.sleep(100); // blocks without tying up OS thread
System.out.println("vthread " + id);
});
}
} // auto-shutdownการเลือกกลุ่มเธรดที่เหมาะสม
แนวทางมีดังนี้
- งานที่ใช้ CPU เป็นหลัก: กลุ่มแบบคงที่ โดย N = จำนวนคอร์ของ
CPU - งานที่ใช้การรับส่งข้อมูลเป็นหลัก: กลุ่มแบบแคชหรือเธรดเสมือน
- งานตามลำดับ: ตัวประมวลผลแบบเธรดเดียว
- งานที่กำหนดเวลาหรือทำซ้ำ: กลุ่มเธรดแบบกำหนดเวลา
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService cpuPool = Executors.newFixedThreadPool(cores);
System.out.println("Pool size: " + cores);ThreadPoolExecutor แบบกำหนดเอง
หากต้องการควบคุมอย่างละเอียด ให้สร้าง ThreadPoolExecutor โดยตรง
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
2, // corePoolSize
10, // maximumPoolSize
60L, // keepAliveTime
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100), // work queue
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // rejection policy
);
tpe.submit(() -> System.out.println("Custom pool task"));
tpe.shutdown();การปิดการทำงานอย่างราบรื่น
ปิด ExecutorService เสมอเพื่อคืนเธรด shutdown() จะหยุดรับงานใหม่ แต่ทำงานที่อยู่ในคิวให้เสร็จ ส่วน shutdownNow() จะพยายามยกเลิกงานที่กำลังทำงาน
ExecutorService pool = Executors.newFixedThreadPool(4);
// ... submit tasks ...
pool.shutdown(); // no new tasks
try {
if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
pool.shutdownNow(); // force cancel
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}ExecutorService ในฐานะ AutoCloseable (Java 19 ขึ้นไป)
ใน Java 19 ขึ้นไป ExecutorService จะใช้งาน AutoCloseable ได้ ให้ใช้ทรัพยากรแบบลองใช้แล้วปิดอัตโนมัติเพื่อปิดการทำงานโดยอัตโนมัติ
// Java 19+ (preview), Java 21 stable
try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
pool.submit(() -> System.out.println("task"));
} // pool.close() called: waits for tasks then shuts downนโยบายการปฏิเสธงาน
เมื่อกลุ่มทำงานเต็มความจุ (เธรดทั้งหมดกำลังทำงานและคิวเต็ม) นโยบายการปฏิเสธจะจัดการงานส่วนเกินดังนี้
- AbortPolicy (ค่าเริ่มต้น): ส่ง
RejectedExecutionException - CallerRunsPolicy: ให้เธรดของผู้เรียกทำงานนั้น
- DiscardPolicy: ทิ้งงานนั้นโดยไม่แจ้งเตือน
- DiscardOldestPolicy: ทิ้งงานที่เก่าที่สุดในคิว
ตรวจสอบความเข้าใจอย่างรวดเร็ว
คุณมีงานที่ใช้ CPU เป็นหลักและทำการคำนวณอย่างหนัก กลุ่มเธรดแบบคงที่ควรมีเธรดกี่เธรด
สรุป: ExecutorService และกลุ่มเธรด
ประเด็นสำคัญที่ควรจำ
- กลุ่มแบบคงที่: มีเธรดจำนวน N คงที่ งานเพิ่มเติมจะเข้าคิว
- กลุ่มแบบแคช: เพิ่มขนาดตามต้องการ เธรดที่ว่างจะหมดอายุหลัง 60 วินาที
- เธรดเดียว: รับประกันลำดับการทำงานแบบอนุกรม
- กลุ่มแบบกำหนดเวลา: รองรับงานที่ล่าช้าและงานที่ทำซ้ำ
- เรียกใช้
shutdown()เสมอ และใช้awaitTermination()เพื่อออกจากการทำงานอย่างเรียบร้อย
คำถามที่พบบ่อย
บทเรียน “ExecutorService และชนิดพูลเธรด” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ExecutorService และชนิดพูลเธรด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ExecutorService และชนิดพูลเธรด”
สร้างพูลเธรดแบบคงที่ แบบแคช แบบเธรดเดียว และแบบตั้งเวลา พร้อมทำความเข้าใจเวลาที่ควรใช้แต่ละแบบ คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “ExecutorService และชนิดพูลเธรด” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม
ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ExecutorService และชนิดพูลเธรด
- การส่งงาน: Runnable กับ Callable
- Future และการจัดการข้อผิดพลาด
- ScheduledExecutorService สำหรับงานที่ทำซ้ำ