0Pricing
Java Academy · บทเรียน

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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ExecutorService และชนิดพูลเธรด
  2. การส่งงาน: Runnable กับ Callable
  3. Future และการจัดการข้อผิดพลาด
  4. ScheduledExecutorService สำหรับงานที่ทำซ้ำ
← กลับไปที่ Java Academy