Skip to main content

Command Palette

Search for a command to run...

Bài 3: Các loại ExecutorService

Published
•5 min read•View as Markdown

Trong bài viết này, chúng ta sẽ tìm hiểu về các loại ExecutorService phổ biến và cách sử dụng chúng. Mỗi loại ExecutorService có các đặc điểm và ứng dụng riêng, giúp bạn lựa chọn phù hợp với yêu cầu cụ thể của ứng dụng.

Fixed Thread Pool

FixedThreadPool tạo ra một pool với số lượng thread cố định. Điều này hữu ích khi bạn biết trước số lượng task sẽ không vượt quá một ngưỡng nhất định.

Cách tạo và sử dụng FixedThreadPool
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 1; i <= 5; i++) {
            Runnable worker = new WorkerThread("Task " + i);
            executorService.submit(worker);
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("All tasks finished.");
    }
}

class WorkerThread implements Runnable {
    private String message;

    public WorkerThread(String message) {
        this.message = message;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " (Start) message = " + message);
        processMessage();
        System.out.println(Thread.currentThread().getName() + " (End)");
    }

    private void processMessage() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Trong ví dụ này:

  • FixedThreadPool được tạo với 3 thread.

  • 5 task được gửi đến ExecutorService, trong đó chỉ có 3 task được chạy đồng thời, 2 task còn lại sẽ chờ cho đến khi có thread rảnh.

Cached Thread Pool

CachedThreadPool tạo ra một pool với số lượng thread linh hoạt. Nó tạo thread mới khi cần và tái sử dụng các thread nhàn rỗi. Điều này hữu ích cho các ứng dụng có số lượng task thay đổi không đều.

Cách tạo và sử dụng CachedThreadPool
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 1; i <= 5; i++) {
            Runnable worker = new WorkerThread("Task " + i);
            executorService.submit(worker);
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("All tasks finished.");
    }
}

Trong ví dụ này:

  • CachedThreadPool được tạo ra, cho phép tạo thread mới khi cần thiết và tái sử dụng các thread nhàn rỗi.

  • 5 task được gửi đến ExecutorService và tất cả đều có thể chạy đồng thời nếu cần.

Single Thread Executor

SingleThreadExecutor tạo một executor chỉ có một thread. Điều này hữu ích cho các task cần đảm bảo thứ tự thực thi.

Cách tạo và sử dụng SingleThreadExecutor
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutorExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        for (int i = 1; i <= 5; i++) {
            Runnable worker = new WorkerThread("Task " + i);
            executorService.submit(worker);
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("All tasks finished.");
    }
}

Trong ví dụ này:

  • SingleThreadExecutor đảm bảo rằng các task được thực thi tuần tự theo thứ tự chúng được gửi đến.

Scheduled Executor Service

ScheduledExecutorService mở rộng ExecutorService và cung cấp các khả năng thực thi các task định kỳ hoặc có độ trễ.

Cách tạo và sử dụng ScheduledExecutorService
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorServiceExample {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);

        // Schedule a task to run after a delay of 3 seconds
        scheduledExecutorService.schedule(new WorkerThread("Task with delay"), 3, TimeUnit.SECONDS);

        // Schedule a task to run repeatedly every 2 seconds
        scheduledExecutorService.scheduleAtFixedRate(new WorkerThread("Repeating Task"), 1, 2, TimeUnit.SECONDS);

        // Schedule a task to run repeatedly with a fixed delay
        scheduledExecutorService.scheduleWithFixedDelay(new WorkerThread("Fixed Delay Task"), 1, 2, TimeUnit.SECONDS);

        // Shutdown the executor after some time to allow tasks to run
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        scheduledExecutorService.shutdown();
        try {
            scheduledExecutorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("All tasks finished.");
    }
}

Trong ví dụ này:

  • ScheduledExecutorService được tạo với 3 thread.

  • Một task được lập lịch để chạy sau 3 giây.

  • Một task được lập lịch để chạy định kỳ mỗi 2 giây.

  • Một task khác được lập lịch để chạy định kỳ với độ trễ cố định sau mỗi lần chạy.

Kết luận

Trong bài viết này, chúng ta đã tìm hiểu về các loại ExecutorService phổ biến: FixedThreadPool, CachedThreadPool, SingleThreadExecutor, và ScheduledExecutorService. Mỗi loại có các đặc điểm và ứng dụng riêng, giúp bạn lựa chọn phù hợp với yêu cầu cụ thể của ứng dụng.

Bài tập thực hành

  1. Tạo một FixedThreadPool với 4 thread và gửi 10 task đồng thời, mỗi task sẽ in ra số thứ tự của nó và tên thread thực thi.

  2. Sử dụng CachedThreadPool để gửi 15 task đồng thời và in ra số thứ tự của từng task và tên thread thực thi.

  3. Tạo một ScheduledExecutorService để lập lịch một task chạy sau 5 giây và một task chạy định kỳ mỗi 3 giây.

Lời khuyên và Best Practices

  • Chọn loại ExecutorService phù hợp: Xem xét yêu cầu cụ thể của ứng dụng để chọn loại ExecutorService tối ưu.

  • Tối ưu hóa kích thước pool: Với FixedThreadPool, cân nhắc số lượng thread phù hợp để tránh tình trạng thừa hoặc thiếu tài nguyên.

  • Quản lý vòng đời của Executor: Luôn gọi shutdown() và awaitTermination() để đảm bảo các task hoàn thành một cách an toàn trước khi ứng dụng kết thúc.

More from this blog

devngu

169 posts