Bài 1: Giới thiệu về ExecutorService
Định nghĩa và Mục đích
ExecutorService là một trong những thành phần quan trọng trong thư viện java.util.concurrent của Java. Nó cung cấp một framework để quản lý các thread, giúp đơn giản hóa việc thực thi các tác vụ đồng thời mà không cần phải quản lý trực tiếp các thread. ExecutorService thực hiện nhiệm vụ phân phối các tác vụ cho một pool các thread, giúp tối ưu hóa hiệu suất và sử dụng tài nguyên hệ thống một cách hiệu quả.
Mục đích
Quản lý thread một cách tự động: Không cần phải tạo và quản lý từng thread riêng lẻ.
Tái sử dụng thread: Giảm chi phí tạo thread bằng cách tái sử dụng các thread đã tồn tại trong pool.
Kiểm soát tốt hơn: Cung cấp các phương thức để kiểm soát và quản lý vòng đời của các task và thread.
Lợi ích
Sử dụng ExecutorService mang lại nhiều lợi ích so với việc quản lý thread thủ công:
Đơn giản hóa mã nguồn: Tách biệt việc tạo và quản lý thread khỏi logic nghiệp vụ.
Tối ưu hóa tài nguyên: Sử dụng hiệu quả tài nguyên hệ thống thông qua quản lý pool các thread.
Tăng khả năng mở rộng: Dễ dàng mở rộng để xử lý nhiều task đồng thời mà không cần thay đổi nhiều mã nguồn.
Cách tạo một ExecutorService
Để tạo một ExecutorService, Java cung cấp lớp tiện ích Executors với các phương thức tạo ra các loại ExecutorService khác nhau.
Fixed Thread Pool
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
Fixed Thread Pool 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.
Cached Thread Pool
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
Cached Thread Pool 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.
Single Thread Executor
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
Single Thread Executor 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.
Scheduled Executor Service
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
Scheduled Executor Service tạo ra một pool với khả năng thực thi các task định kỳ hoặc có độ trễ.
Ví dụ Cụ thể
Dưới đây là một ví dụ đơn giản sử dụng Fixed Thread Pool để thực thi các task đồng thời:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorServiceExample {
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.execute(worker);
}
executorService.shutdown();
while (!executorService.isTerminated()) {
}
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:
ExecutorService được tạo với một fixed thread pool có 3 thread.
WorkerThread là một lớp triển khai
Runnableđể thực hiện các task.ExecutorService thực thi 5 task đồng thời với pool 3 thread, mỗi thread sẽ thực thi một
WorkerThread.
Kết luận
Trong bài viết này, chúng ta đã tìm hiểu về ExecutorService, một công cụ mạnh mẽ trong việc quản lý và thực thi các tác vụ đồng thời trong Java. Chúng ta đã thảo luận về định nghĩa, mục đích, lợi ích và cách tạo một ExecutorService với các loại khác nhau. Trong các bài viết tiếp theo, chúng ta sẽ đi sâu vào cách sử dụng ExecutorService, quản lý kết quả, xử lý ngoại lệ, và nhiều hơn nữa.
Bài tập thực hành
Tạo một
ExecutorServicesử dụngCached Thread Poolvà chạy 10 task đồng thời, mỗi task in ra số thứ tự của nó và tên thread thực thi.Sử dụng
ScheduledExecutorServiceđể tạo một task chạy định kỳ mỗi 1 giây và in ra thời gian hiện tại.
Lời khuyên và Best Practices
Luôn luôn gọi
shutdown()trênExecutorServiceđể đảm bảo rằng các thread được tắt một cách an toàn sau khi hoàn thành công việc.Sử dụng
Futuređể quản lý kết quả và xử lý ngoại lệ từ các task chạy đồng thời.Chọn loại
ExecutorServicephù hợp với nhu cầu cụ thể của ứng dụng để tối ưu hóa hiệu suất.