Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Java

Khác biệt giữa ScheduledThreadPool và SingleThreadExecutor trong Java

Cả hai đều là ExecutorService do Executors factory tạo ra, nhưng phục vụ mục đích lập lịch (scheduling) và thực thi tuần tự (sequential) khác nhau.

Bảng so sánh

Tiêu chíScheduledThreadPoolExecutorSingleThreadExecutor
Factory methodExecutors.newScheduledThreadPool(corePoolSize)Executors.newSingleThreadExecutor()
Đặc điểm chínhLên lịch chạy task sau delay hoặc định kỳ (periodic)Thực thi task tuần tự, đảm bảo chỉ 1 task active tại một thời điểm
QueueDelayedWorkQueue (priority queue theo thời gian)LinkedBlockingQueue (unbounded FIFO)
Thread pool sizeFixed corePoolSize (có thể config maxPoolSize)Luôn 1 thread (core=max=1)
Thread lifetimeCore threads sống vĩnh viễn (keepAliveTime=0 mặc định). Non-core threads timeout.Thread duy nhất sống vĩnh viễn. Nếu chết do exception → tạo thread mới thay thế.
Phương thức đặc thùschedule, scheduleAtFixedRate, scheduleWithFixedDelayChỉ có methods của ExecutorService (submit, execute, invokeAll...)
Use caseCron-like jobs, periodic cleanup, heartbeat, retry với delay, rate limitingSequential processing, serialization point, task ordering guarantee, offloading từ main thread
ImplementsScheduledExecutorService (extends ExecutorService)ExecutorService (wrapped FinalizableDelegatedExecutorService)

Ví dụ ScheduledThreadPool

import java.util.concurrent.*;

public class ScheduledThreadPoolDemo {
    public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
        
        // 1. Chạy một lần sau 5 giây
        scheduler.schedule(() -> System.out.println("One-shot task at " + System.currentTimeMillis()), 
                           5, TimeUnit.SECONDS);
        
        // 2. Chạy định kỳ: fixed-rate (bắt đầu lần sau sau period từ lần trước bắt đầu)
        ScheduledFuture fixedRate = scheduler.scheduleAtFixedRate(
            () -> System.out.println("Fixed-rate: " + System.currentTimeMillis()),
            2, 3, TimeUnit.SECONDS); // initialDelay=2s, period=3s
        
        // 3. Chạy định kỳ: fixed-delay (khoảng cách giữa cuối task này và đầu task sau)
        ScheduledFuture fixedDelay = scheduler.scheduleWithFixedDelay(
            () -> {
                System.out.println("Fixed-delay start: " + System.currentTimeMillis());
                try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
                System.out.println("Fixed-delay end: " + System.currentTimeMillis());
            },
            1, 2, TimeUnit.SECONDS); // initialDelay=1s, delay=2s
        
        // Hủy sau 15 giây
        scheduler.schedule(() -> {
            fixedRate.cancel(false);
            fixedDelay.cancel(false);
            scheduler.shutdown();
            System.out.println("Scheduler shutdown");
        }, 15, TimeUnit.SECONDS);
    }
}

Ví dụ SingleThreadExecutor

import java.util.concurrent.*;

public class SingleThreadExecutorDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        // Submit tasks – thực thi theo thứ tự submit (FIFO)
        for (int i = 1; i <= 5; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " started on " + Thread.currentThread().getName());
                try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
                System.out.println("Task " + taskId + " finished");
                return taskId * 2;
            });
        }
        
        // Shutdown gracefully
        executor.shutdown();
        try {
            if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

Lưu ý quan trọng

  • ScheduledThreadPool với corePoolSize > 1 có thể chạy nhiều task song song NẾU chúng đến hạn cùng lúc. Muốn serializable scheduled tasks → dùng corePoolSize = 1.
  • SingleThreadExecutor đảm bảo happens-before giữa các task (memory visibility). Task n+1 thấy tất cả write của task n.
  • Cả hai đều dùng unbounded queue → rủi ro OOM nếu producer nhanh hơn consumer liên tục. Production nên dùng ThreadPoolExecutor custom với bounded queue + RejectedExecutionHandler.
  • Shutdown: Luôn gọi shutdown() → awaitTermination() → shutdownNow() nếu cần.