Java - Trình Lập Lịch Thread

Xin chào các nhà phép thuật Java tương lai! Hôm nay, chúng ta sẽ bắt đầu hành trình phiêu lưu vào thế giới lập lịch thread Java. Đừng lo nếu bạn mới bắt đầu học lập trình – tôi sẽ là người hướng dẫn bạn thân thiện, và chúng ta sẽ làm việc với chủ đề này bước từng bước. Vậy hãy lấy ra cây búa ảo của bạn (bàn phím), và hãy bắt đầu!

Java - Thread Scheduler

Thread Scheduling là gì?

Trước khi chúng ta nhảy vào chi tiết, hãy hiểu rõ về việc lập lịch thread là gì. Hãy tưởng tượng bạn là một người điều khiển cirque (đó là runtime Java), và bạn có nhiều diễn viên (threads) chờ đợi để trình diễn. Công việc của bạn là quyết định diễn viên nào sẽ lên sân và trong bao lâu. Đó chính là điều gì việc lập lịch thread làm trong Java – nó quản lý nhiều threads và quyết định thread nào sẽ chạy và khi nào.

Tại sao chúng ta cần Thread Scheduling?

Bạn có thể hỏi, "Tại sao chúng ta không để tất cả các threads chạy khi họ muốn?" Nhưng hãy tưởng tượng nếu tất cả các diễn viên cirque cố gắng trình diễn cùng một lúc – sẽ có sự hỗn loạn! Việc lập lịch thread giúp duy trì thứ tự, đảm bảo sự phân bổ tài nguyên công bằng và cải thiện hiệu suất tổng thể của hệ thống.

Trình Lập Lịch Thread của Java

Java có một trình lập lịch thread tích hợp sẵn để xử lý nhiệm vụ phức tạp này cho chúng ta. Nó sử dụng kết hợp giữa việc lập lịch cấp độ hệ điều hành và các thuật toán riêng để quản lý threads một cách hiệu quả.

Trạng thái của Thread

Trước khi chúng ta nhảy vào lập lịch, hãy nhanh chóng xem lại các trạng thái mà một thread có thể ở trong:

  1. New
  2. Runnable
  3. Running
  4. Blocked/Waiting
  5. Terminated

Trình lập lịch chịu trách nhiệm cho việc di chuyển các threads giữa các trạng thái này.

Tạo và Lập Lịch Thread Cơ Bản

Hãy bắt đầu với một ví dụ đơn giản để xem Java tạo và lập lịch threads như thế nào:

public class SimpleThreadExample {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 1: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 2: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

thread1.start();
thread2.start();
}
}

Trong ví dụ này, chúng ta tạo hai threads in ra các số từ 0 đến 4. Lời gọi Thread.sleep(1000) làm mỗi thread tạm dừng 1 giây giữa các lần in.

Khi bạn chạy chương trình này, bạn sẽ thấy rằng đầu ra có thể không phải là thứ tự lắng nhau hoàn hảo. Đó là vì trình lập lịch Java đang quyết định khi nào để chuyển đổi giữa các threads!

Mức Ưu Tiên của Thread

Java cho phép chúng ta đưa ra lời khuyên cho trình lập lịch về các threads quan trọng hơn. Chúng ta có thể đặt mức ưu tiên của thread bằng cách sử dụng phương thức setPriority(). Hãy điều chỉnh ví dụ trước đó của chúng ta:

public class ThreadPriorityExample {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Low Priority Thread: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("High Priority Thread: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);

thread1.start();
thread2.start();
}
}

Trong ví dụ này, chúng ta đặt thread1 có mức ưu tiên thấp nhất và thread2 có mức ưu tiên cao nhất. Mặc dù điều này không đảm bảo rằng thread2 sẽ luôn chạy trước hoặc chạy nhiều hơn, nó đưa ra lời khuyên cho trình lập lịch rằng nó nên ưu tiên thread2 khi có thể.

ScheduledExecutorService

Bây giờ, hãy xem cách lập lịch threads một cách nâng cao hơn bằng cách sử dụng ScheduledExecutorService. Công cụ mạnh mẽ này cho phép chúng ta lập lịch các nhiệm vụ chạy sau một khoảng trễ hoặc theo các khoảng thời gian cố định.

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

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

Runnable task1 = () -> System.out.println("Task 1 executed at: " + System.currentTimeMillis());
Runnable task2 = () -> System.out.println("Task 2 executed at: " + System.currentTimeMillis());

executor.schedule(task1, 5, TimeUnit.SECONDS);
executor.scheduleAtFixedRate(task2, 0, 2, TimeUnit.SECONDS);

try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}

executor.shutdown();
}
}

Trong ví dụ này:

  • Chúng ta tạo một ScheduledExecutorService với 2 threads.
  • task1 được lập lịch chạy một lần sau 5 giây trễ.
  • task2 được lập lịch chạy mỗi 2 giây, bắt đầu ngay lập tức.
  • Chúng ta để chương trình chạy trong 10 giây trước khi tắt executor.

Điều này minh họa cách chúng ta có thể kiểm soát chính xác khi và thường xuyên nhiệm vụ của chúng ta sẽ chạy.

Các Phương Thức của ScheduledExecutorService

Dưới đây là bảng các phương thức chính được cung cấp bởi ScheduledExecutorService:

Method Mô tả
schedule(Runnable, long, TimeUnit) Lập lịch một nhiệm vụ một lần chạy sau một khoảng trễ cụ thể
scheduleAtFixedRate(Runnable, long, long, TimeUnit) Lập lịch một nhiệm vụ chạy định kỳ, với một khoảng trễ cố định giữa bắt đầu của mỗi thực hiện
scheduleWithFixedDelay(Runnable, long, long, TimeUnit) Lập lịch một nhiệm vụ chạy định kỳ, với một khoảng trễ cố định giữa cuối một thực hiện và bắt đầu thực hiện tiếp theo

Kết Luận

Và thế là xong, các bạn! Chúng ta đã hành trình qua các khái niệm cơ bản của lập lịch thread Java, từ việc tạo thread đơn giản đến việc lập lịch nâng cao với ScheduledExecutorService. Hãy nhớ, việc lập lịch thread như làm việc của một nhà dẫn nhạc – tất cả liên quan đến sự hợp nhất và thời điểm.

Khi bạn tiếp tục hành trình Java của mình, bạn sẽ khám phá thêm nhiều cách để tinh chỉnh hành vi của thread. Nhưng bây giờ, hãy khen ngợi bản thân mình – bạn đã bước ra một bước lớn vào thế giới lập trình đồng thời!

Hãy tiếp tục tập luyện, giữ được sự tò mò và quan trọng nhất, hãy có niềm vui khi mã hóa! Ai biết, có lẽ một ngày nào đó bạn sẽ viết thế hệ mới của các trình lập lịch thread Java. Đến khi đó, hãy chúc mừng.threading!

Credits: Image by storyset