ThreadPoolExecutor - 1

"Các lập trình viên bình thường sớm hay muộn cũng hiểu được thực tế là họ có nhiều nhiệm vụ nhỏ cần được thực hiện theo thời gian."

"Nếu bạn đang viết một trò chơi, thì đó là hành động mà từng nhân vật thực hiện."

"Nếu bạn đang viết một máy chủ web, thì đó là các lệnh khác nhau đến từ người dùng: tải ảnh lên, chuyển mã ảnh sang định dạng mong muốn, áp dụng mẫu mong muốn, v.v."

"Sớm hay muộn, tất cả các nhiệm vụ lớn đều được chia thành một tập hợp các nhiệm vụ nhỏ, có thể quản lý được."

"Vì vậy, với bối cảnh này, một câu hỏi tế nhị được đặt ra: bạn phải quản lý tất cả chúng như thế nào? Điều gì sẽ xảy ra nếu bạn cần thực hiện hàng trăm tác vụ trong một phút? Tạo một chuỗi cho mỗi tác vụ sẽ không có nhiều ý nghĩa. Máy Java phân bổ khá nhiều tài nguyên cho mỗi luồng."

"Nói cách khác, việc tạo và hủy một luồng có thể tốn nhiều thời gian và tài nguyên hơn so với bản thân nhiệm vụ."

"Những người sáng tạo Java đã đưa ra một giải pháp hay cho vấn đề này: ThreadPoolExecutor .

" ThreadPoolExecutor là một lớp có hai nội dung bên trong:"

A)  Một hàng đợi nhiệm vụ mà bạn có thể thêm nhiệm vụ vào khi chúng phát sinh trong chương trình.

B) Một nhóm luồng, là một nhóm các luồng thực hiện các tác vụ này.

"Hơn nữa, các luồng không bị hủy sau khi một nhiệm vụ hoàn thành. Thay vào đó, chúng sẽ ngủ để sẵn sàng bắt đầu một nhiệm vụ mới ngay khi nó xuất hiện."

"Khi bạn tạo một ThreadPoolExecutor , bạn có thể đặt số lượng luồng tối đa được tạo và số lượng tác vụ tối đa có thể được xếp hàng đợi. Nói cách khác, bạn có thể giới hạn số lượng luồng là 10 chẳng hạn và số lượng nhiệm vụ xếp hàng đến 100."

"Đây là cách ThreadPoolExecutor hoạt động:"

1)  Khi bạn thêm một tác vụ mới, nó sẽ được đặt ở cuối hàng đợi.

2)  Nếu hàng đợi đầy, một ngoại lệ sẽ được đưa ra.

3)  Sau khi hoàn thành một tác vụ, mỗi luồng sẽ nhận tác vụ tiếp theo từ hàng đợi và bắt đầu thực thi tác vụ đó.

4) Nếu không có tác vụ nào trong hàng đợi, chuỗi sẽ chuyển sang chế độ ngủ cho đến khi tác vụ mới được thêm vào.

"Cách tiếp cận, trong đó chúng tôi giới hạn số lượng luồng công nhân, có lợi ở chỗ chúng tôi càng có nhiều luồng, chúng càng can thiệp lẫn nhau. Sẽ hiệu quả hơn nhiều khi có 5-10 luồng công nhân và một hàng dài các tác vụ hơn là để tạo 100 luồng cho một loạt các tác vụ, các tác vụ này sẽ cạnh tranh với nhau về tài nguyên: bộ nhớ, thời gian của bộ xử lý, quyền truy cập cơ sở dữ liệu, v.v."

"Đây là một ví dụ về ThreadPoolExecutor đang hoạt động:"

Ví dụ
ExecutorService service = Executors.newFixedThreadPool(2);

for(int i = 0; i < 10; i++)
{
 service.submit(new Runnable() {
    public void run()
    {
     // Here we download something big from the Internet.
    }
 });
}

“À, tôi không thấy…”

" Đối tượng ThreadPoolExecutor được tạo khi phương thức newFixedThreadPool được gọi."

Vì vậy, nó rất dễ sử dụng. Ngay khi bạn thêm một tác vụ vào nó bằng phương thức gửi, nó sẽ:

A)  Đánh thức một luồng đang ngủ, nếu có, để thực thi tác vụ.

B)  Nếu không có luồng đang chờ, thì nó sẽ tạo một luồng mới cho tác vụ.

C)  Nếu đạt đến số luồng tối đa, thì nó chỉ cần đặt tác vụ ở cuối hàng đợi.

"Tôi đã cố tình đưa ví dụ «ở đây chúng tôi tải xuống thứ gì đó có dung lượng lớn từ Internet». Nếu chúng tôi có 100 tác vụ «tải xuống thứ gì đó có dung lượng lớn từ Internet», thì sẽ không có ý nghĩa gì khi chạy nhiều chúng cùng một lúc—chúng tôi' sẽ bị hạn chế bởi giới hạn băng thông kết nối Internet của chúng ta. Trong trường hợp này, một vài luồng là đủ. Đây là những gì bạn thấy trong ví dụ trên:"

ExecutorService service = Executors.newFixedThreadPool(2);

"Hóa ra làm việc với một đống nhiệm vụ không quá khó."

"Phải. Thậm chí còn dễ hơn bạn tưởng. Nhưng Kim sẽ kể cho bạn nghe về điều đó."