Java - Thread Pools

Chào bạn, những nhà phát triển Java tương lai! Hôm nay, chúng ta sẽ bơi lội vào thế giới thú vị của Thread Pools. Đừng lo nếu bạn mới bắt đầu học lập trình; tôi sẽ hướng dẫn bạn qua khái niệm này bước từng bước, như cách tôi đã làm cho nhiều học sinh khác trong những năm dạy học. Vậy, hãy lấy ly cà phê (hoặc trà, nếu bạn thích), và hãy bắt đầu nào!

Java - Thread Pools

Thread Pools là gì?

Hãy tưởng tượng bạn đang quản lý một nhà hàng hoành tráng. Mỗi khi có khách hàng mới đến, bạn có thể thuê một phục vụ mới để phục vụ họ. Nhưng điều đó sẽ rối loạn và chi phí cao! Thay vì, bạn có một số phục vụ cố định để phục vụ nhiều khách hàng. Đó chính là điều gì một thread pool làm trong lập trình Java.

Một thread pool là một nhóm các thread đã khởi tạo trước, có thể tái sử dụng để thực hiện các nhiệm vụ. Thay vì tạo một thread mới cho mỗi nhiệm vụ, điều này có thể tiêu tốn tài nguyên, chúng ta sử dụng một pool của các thread hiện có để thực hiện các nhiệm vụ này.

Tại sao nên sử dụng Thread Pools trong Java?

Bạn có thể hỏi, "Tại sao phải mắc một đầu với thread pools? Ta không thể chỉ tạo các thread mới khi cần?" Để giải thích, hãy để tôi kể một câu chuyện nhỏ từ kinh nghiệm dạy học của mình.

Một lần, tôi có một học sinh nỗ lực tạo một thread mới cho mỗi nhiệm vụ trong ứng dụng của anh ấy. Chương trình của anh ấy hoạt động tốt cho các đầu vào nhỏ, nhưng khi anh ấy cố gắng xử lý một tập dữ liệu lớn, máy tính của anh ấy gần như sập! Đó là lúc tôi giới thiệu anh ấy với thread pools.

Dưới đây là một số lý do chính để sử dụng thread pools:

  1. Hiệu suất tốt hơn: Việc tạo và hủy thread là chi phí cao. Thread pools tái sử dụng các thread, tiết kiệm chi phí.
  2. Quản lý tài nguyên: Bạn có thể kiểm soát số lượng thread tối đa có thể chạy đồng thời.
  3. Độ phản hồi tốt hơn: Các nhiệm vụ có thể được thực hiện ngay lập tức bởi một thread đang chạy.

Tạo Thread Pools trong Java

Java cung cấp nhiều cách để tạo thread pools thông qua lớp Executors. Hãy xem xét ba phương pháp phổ biến:

1. Tạo Thread Pool Sử Dụng Phương Thức newFixedThreadPool()

Phương thức này tạo một thread pool với số lượng thread cố định. Hãy xem một ví dụ:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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

for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executor.execute(worker);
}

executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Hoàn thành tất cả các thread");
}
}

class WorkerThread implements Runnable {
private String message;

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

public void run() {
System.out.println(Thread.currentThread().getName() + " (Bắt đầu) message = " + message);
processMessage();
System.out.println(Thread.currentThread().getName() + " (Kết thúc)");
}

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

Trong ví dụ này, chúng ta tạo một fixed thread pool với 5 thread. Sau đó, chúng ta gửi 10 nhiệm vụ này cho pool. Pool sẽ sử dụng 5 thread của mình để thực hiện 10 nhiệm vụ này, tái sử dụng các thread khi chúng có sẵn.

2. Tạo Thread Pool Sử Dụng Phương Thức newCachedThreadPool()

Phương thức này tạo một thread pool tạo các thread mới khi cần, nhưng sẽ tái sử dụng các thread đã được xây dựng trước khi chúng có sẵn. Hãy xem cách nó hoạt động:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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

for (int i = 0; i < 100; i++) {
executor.execute(new Task());
}

executor.shutdown();
}
}

class Task implements Runnable {
public void run() {
System.out.println("Thread Name: " + Thread.currentThread().getName());
}
}

Trong ví dụ này, chúng ta tạo một cached thread pool và gửi 100 nhiệm vụ cho nó. Pool sẽ tạo các thread mới khi cần và tái sử dụng chúng khi có thể.

3. Tạo Thread Pool Sử Dụng Phương Thức newScheduledThreadPool()

Phương thức này tạo một thread pool có thể lên lịch các lệnh chạy sau một khoảng đợi nhất định, hoặc thực hiện định kỳ. Dưới đây là một ví dụ:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolExample {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

Runnable task = () -> System.out.println("Thực hiện nhiệm vụ tại " + System.nanoTime());

executor.scheduleAtFixedRate(task, 0, 2, TimeUnit.SECONDS);
}
}

Trong ví dụ này, chúng ta tạo một scheduled thread pool với một thread. Sau đó, chúng ta lên lịch một nhiệm vụ để chạy mỗi 2 giây.

Các Phương Thức Trong ExecutorService

Dưới đây là bảng các phương thức quan trọng trong giao diện ExecutorService:

Phương thức Mô tả
execute(Runnable) Thực hiện lệnh được chỉ định vào một thời điểm trong tương lai
submit(Callable) Gửi một nhiệm vụ trả về giá trị để thực hiện và trả về một Future
shutdown() Khởi động việc tắt máy theo thứ tự trong đó các nhiệm vụ đã gửi trước được thực hiện, nhưng không có nhiệm vụ mới sẽ được chấp nhận
shutdownNow() Thử dừng tất cả các nhiệm vụ đang thực hiện tích cực và ngưng xử lý các nhiệm vụ đang chờ
isShutdown() Trả về true nếu executor này đã bị tắt
isTerminated() Trả về true nếu tất cả các nhiệm vụ đã hoàn thành sau khi tắt máy

Kết Luận

Và thế là xong, các bạn! Chúng ta đã hành trình qua đất nước của Java Thread Pools, từ việc hiểu tại sao chúng lại hữu ích đến việc tạo và sử dụng chúng theo nhiều cách khác nhau. Hãy nhớ, như trong câu so sánh nhà hàng của chúng ta, thread pools giúp chúng ta quản lý tài nguyên một cách hiệu quả.

Trong những năm dạy học, tôi đã thấy các học sinh từ việc gặp khó khăn với các khái niệm cơ bản về threading đến việc xây dựng các ứng dụng đa luồng phức tạp và hiệu quả. Với thời gian và kiên nhẫn, bạn cũng sẽ đến được đó!

Khi tiếp tục hành trình của mình với Java, hãy tiếp tục khám phá và thử nghiệm. Thread pools chỉ là một công cụ trong bộ công cụ rộng lớn mà Java cung cấp cho lập trình đồng thời. Chúc các bạn mãi mãi có các thread được kết hợp một cách hiệu quả!

Credits: Image by storyset