ทั้งคู่มาจาก Executors factory methods แต่มีจุดประสงค์และพฤติกรรมต่างกัน
ตารางเปรียบเทียบ
| ลำดับ | หัวข้อ | ScheduledThreadPool | SingleThreadExecutor |
| 1 | วัตถุประสงค์ | รัน Task **ตามเวลา** (delay, periodic, fixed rate) | รัน Task **แบบ Sequential** (ทีละตัว, เรียงลำดับ) |
| 2 | Factory Method | Executors.newScheduledThreadPool(n) | Executors.newSingleThreadExecutor() |
| 3 | Queue Type | DelayedWorkQueue (Priority queue by time) | LinkedBlockingQueue (FIFO) |
| 4 | Thread Count | Fixed core threads (n), สร้างเพิ่มได้ถ้า queue เต็ม | **คงที่ 1 thread** ตลอด |
| 5 | Task Types | Runnable, Callable + **ScheduledFuture** | Runnable, Callable |
| 6 | Scheduling | schedule(), scheduleAtFixedRate(), scheduleWithFixedDelay() | ไม่มี (submit แล้วรันทันทีตามลำดับ) |
| 7 | Thread Lifetime | Core threads อยู่ตลอด (keepAliveTime=0) | Recreate thread ถ้าตาย (เพื่อรักษา sequential) |
| 8 | Ordering Guarantee | ไม่รับประกันลำดับ (ขึ้นกับเวลา) | **รับประกัน FIFO order** อย่างเคร่งครัด |
| 9 | Use Case | Cron jobs, Periodic tasks, Delayed execution | Sequential processing, Serialization, Single-threaded context |
ScheduledThreadPoolExecutor - ตัวอย่าง
import java.util.concurrent.*;
public class ScheduledThreadPoolExample {
public static void main(String[] args) throws InterruptedException {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 1. One-time delay: รันหลัง 3 วินาที
scheduler.schedule(() -> System.out.println("Task 1: Run after 3s"), 3, TimeUnit.SECONDS);
// 2. Fixed Rate: รันทุก 2 วินาที (เริ่มนับจากเวลาเริ่มต้น)
// ถ้า task ใช้เวลานานกว่า period → รันซ้อนกัน (multi-thread)
ScheduledFuture> fixedRate = scheduler.scheduleAtFixedRate(
() -> System.out.println("FixedRate: " + System.currentTimeMillis()),
1, 2, TimeUnit.SECONDS
);
// 3. Fixed Delay: รันทุก 2 วินาที **หลังจบ task ก่อนหน้า**
// ไม่รันซ้อนกัน มั่นใจได้ว่า gap ระหว่าง task = 2s
ScheduledFuture> fixedDelay = scheduler.scheduleWithFixedDelay(
() -> {
System.out.println("FixedDelay start: " + System.currentTimeMillis());
try { Thread.sleep(1500); } catch (InterruptedException ignored) {}
System.out.println("FixedDelay end: " + System.currentTimeMillis());
},
1, 2, TimeUnit.SECONDS
);
// หยุดหลัง 10 วินาที
Thread.sleep(10000);
fixedRate.cancel(false);
fixedDelay.cancel(false);
scheduler.shutdown();
}
}
Output (ตัวอย่าง)
FixedRate: 1000
FixedDelay start: 1000
FixedDelay end: 2501
FixedRate: 3001
FixedRate: 5001
FixedDelay start: 4501
Task 1: Run after 3s
FixedDelay end: 6001
FixedRate: 7001
FixedDelay start: 8001
FixedDelay end: 9501
FixedRate: 9001
SingleThreadExecutor - ตัวอย่าง
import java.util.concurrent.*;
public class SingleThreadExecutorExample {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
// Submit tasks - จะรันตามลำดับ FIFO ใน thread เดียว
Future> f1 = executor.submit(() -> {
System.out.println("Task 1 start: " + Thread.currentThread().getName());
sleep(1000);
System.out.println("Task 1 end");
});
Future> f2 = executor.submit(() -> {
System.out.println("Task 2 start: " + Thread.currentThread().getName());
sleep(500);
System.out.println("Task 2 end");
});
Future> f3 = executor.submit(() -> {
System.out.println("Task 3 start: " + Thread.currentThread().getName());
sleep(200);
System.out.println("Task 3 end");
});
// รอให้เสร็จทั้งหมด
f1.get(); f2.get(); f3.get();
executor.shutdown();
System.out.println("All tasks completed sequentially");
}
static void sleep(long ms) {
try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
}
Output
Task 1 start: pool-1-thread-1
Task 1 end
Task 2 start: pool-1-thread-1
Task 2 end
Task 3 start: pool-1-thread-1
Task 3 end
All tasks completed sequentially
Key Differences Deep Dive
Queue Behavior
- ScheduledThreadPool:
DelayedWorkQueue - เรียงตาม **trigger time** (priority queue)
- SingleThreadExecutor:
LinkedBlockingQueue - **FIFO** insertion order
Thread Death Handling
- ScheduledThreadPool: Core threads ไม่ตาย (keepAliveTime=0) - thread pool size คงที่
- SingleThreadExecutor: ถ้า thread ตาย (exception) → สร้าง thread ใหม่ทันที เพื่อรักษา sequential execution
Shutdown Behavior
// ScheduledThreadPool
scheduler.shutdown(); // รอ scheduled tasks ที่รออยู่ (ไม่รัน task ใหม่)
scheduler.shutdownNow(); // ยกเลิก scheduled tasks, return list ของ tasks ที่ยังไม่รัน
// SingleThreadExecutor
executor.shutdown(); // รอ tasks ใน queue
executor.shutdownNow(); // Interrupt running task, return remaining queue
สรุป: เลือกอะไรดี?
| สถานการณ์ | เลือก | เหตุผล |
| Cron job / Scheduled task | ScheduledThreadPool | Built-in scheduling APIs |
| Periodic cleanup / Heartbeat | ScheduledThreadPool | Fixed rate/delay scheduling |
| Task queue ที่ต้องรันทีละตัวเรียงลำดับ | SingleThreadExecutor | Strict FIFO, single thread |
| Serialization / Non-thread-safe resource | SingleThreadExecutor | Confine access to 1 thread |
| Event loop pattern | SingleThreadExecutor | Sequential event processing |
| Delayed one-time tasks | ScheduledThreadPool | schedule(delay) |
Modern Alternative: ScheduledExecutorService vs CompletableFuture
// Java 9+ - ใช้ CompletableFuture สำหรับ delayed task แบบง่าย
CompletableFuture.delayedExecutor(5, TimeUnit.SECONDS)
.execute(() -> System.out.println("Run after 5 seconds"));
// หรือ ScheduledExecutorService จาก Executors.newSingleThreadScheduledExecutor()
// รวมคุณสมบัติทั้งสอง: Single thread + Scheduling