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

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

```java
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

```java
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

```java
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

```java
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.
