Java - Điều khiển Thread

Xin chào các pháp sư Java tương lai! Hôm nay, chúng ta sẽ cùng khám phá thế giới kỳ diệu của Điều khiển Thread trong Java. Đừng lo lắng nếu bạn là người mới bắt đầu lập trình - tôi sẽ dẫn dắt bạn từng bước trong hành trình này, giống như tôi đã làm cho biết bao sinh viên trong những năm dạy học của mình. Vậy, hãy cầm lấy pháp杖 ảo của bạn (bàn phím) và cùng tạo ra phép màu lập trình nào!

Java - Thread Control

Thread là gì?

Trước khi chúng ta nhảy vào việc điều khiển thread, hãy hiểu thread là gì. Hãy tưởng tượng bạn đang ở trong bếp, nấu một bữa ăn phức tạp. Bạn là chương trình chính, và mỗi nhiệm vụ bạn đang làm (băm rau, khuấy nước sốt, kiểm tra lò) đều giống như một thread. Chúng đều là một phần của cùng một quy trình tổng thể (nấu cơm), nhưng là các nhiệm vụ riêng biệt có thể xảy ra đồng thời.

Trong Java, một thread là một tiến trình nhẹ, là đơn vị xử lý nhỏ nhất. Đó là cách để tạo ra một đường dẫn thực thi riêng cho một nhiệm vụ cụ thể trong chương trình của bạn.

Tại sao cần Điều khiển Thread?

Vậy, tại sao chúng ta lại muốn điều khiển những thread này? Hãy tiếp tục với ví dụ nấu ăn. Đôi khi, bạn cần dừng việc khuấy nước sốt để kiểm tra thịt nướng. Or bạn có thể cần chờ nước sôi trước khi thêm mì. Tương tự, trong lập trình, chúng ta thường cần điều khiển luồng và thời gian của các thread khác nhau để đảm bảo chương trình của chúng ta chạy mượt mà và hiệu quả.

Các phương thức Điều khiển Thread trong Java

Java cung cấp nhiều phương thức để điều khiển thread. Hãy cùng xem chúng trong bảng sau:

Phương thức Mô tả
start() Khởi động thread, gây ra việc gọi phương thức run()
run() Chứa mã cấu thành nhiệm vụ của thread
sleep() Gây cho thread dừng lại trong một khoảng thời gian xác định
join() Chờ thread kết thúc
yield() Gây cho thread暂停 lại tạm thời và cho phép các thread khác thực thi
interrupt() Ngắt thread, gây ra việc dừng lại những gì thread đang làm
isAlive() Kiểm tra xem thread có vẫn đang chạy hay không

Bây giờ, hãy cùng tìm hiểu từng phương thức này với một số ví dụ!

1. start() và run()

Cả hai phương thức này làm việc cùng nhau. Dưới đây là một ví dụ đơn giản:

public class MyThread extends Thread {
public void run() {
System.out.println("My thread is running!");
}

public static void main(String args[]) {
MyThread thread = new MyThread();
thread.start();
}
}

Trong ví dụ này, chúng ta tạo một thread mới và gọi phương thức start() của nó. Điều này, ngược lại, gọi phương thức run(), chứa mã thực thi thực tế của thread.

2. sleep()

Phương thức sleep() giống như việc nhấn nút "dừng lại" trên báo thức. Nó làm cho thread dừng lại trong một khoảng thời gian xác định. Dưới đây là cách nó hoạt động:

public class SleepyThread extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Thread is counting: " + i);
try {
Thread.sleep(1000); // Dừng lại trong 1 giây
} catch (InterruptedException e) {
System.out.println("Thread was interrupted!");
}
}
}

public static void main(String args[]) {
SleepyThread thread = new SleepyThread();
thread.start();
}
}

Thread này sẽ đếm từ 1 đến 5, dừng lại 1 giây giữa mỗi lần đếm. Giống như một người ngủ gà từ từ đếm cừu!

3. join()

Phương thức join() giống như việc chờ bạn của bạn hoàn thành nhiệm vụ trước khi cả hai đi ăn trưa. Nó làm cho thread hiện tại chờ cho đến khi thread được join hoàn thành việc thực thi. Dưới đây là một ví dụ:

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

Thread t2 = new Thread(() -> {
try {
t1.join(); // Chờ t1 hoàn thành
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: I'm done waiting!");
});

t1.start();
t2.start();
}
}

Trong ví dụ này, Thread 2 chờ Thread 1 hoàn thành trước khi in ra thông báo của mình.

4. yield()

Phương thức yield() giống như việc lịch sự trong một cuộc trò chuyện - nó đề xuất thread có thể dừng lại để các thread cùng ưu tiên khác thực thi. Tuy nhiên, nó chỉ là một gợi ý cho bộ điều độ, và có thể bị bỏ qua. Dưới đây là một ví dụ đơn giản:

public class YieldExample implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + " in control");
Thread.yield();
}
}

public static void main(String[] args) {
Thread t1 = new Thread(new YieldExample(), "Thread 1");
Thread t2 = new Thread(new YieldExample(), "Thread 2");
t1.start();
t2.start();
}
}

Bạn có thể thấy các thread thay phiên nhau kiểm soát, nhưng hãy nhớ, điều này không được đảm bảo!

5. interrupt()

Phương thức interrupt() giống như việc gõ vai ai đó để nhận sự chú ý. Nó không dừng thread ngay lập tức nhưng đặt một cờ mà thread có thể kiểm tra. Dưới đây là cách nó hoạt động:

public class InterruptExample implements Runnable {
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Thread is running...");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("Thread was interrupted while sleeping");
}
System.out.println("Thread has finished.");
}

public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new InterruptExample());
thread.start();
Thread.sleep(5000); // Để thread chạy trong 5 giây
thread.interrupt();
}
}

Trong ví dụ này, thread chạy cho đến khi nó bị ngắt sau 5 giây.

6. isAlive()

Phương thức isAlive() giống như việc kiểm tra xem ai đó có vẫn còn ở bàn làm việc hay không. Nó trả về true nếu thread vẫn đang chạy. Dưới đây là một ví dụ nhanh:

public class AliveExample extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("Thread is running: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) throws InterruptedException {
AliveExample thread = new AliveExample();
thread.start();

while (thread.isAlive()) {
System.out.println("Main thread will wait until MyThread is alive");
Thread.sleep(1500);
}
System.out.println("Main thread is run to completion");
}
}

Ví dụ này cho thấy thread chính chờ và kiểm tra xem thread tùy chỉnh có vẫn còn sống hay không.

Kết luận

Và thế là bạn đã có tất cả! Chúng ta đã cùng nhau hành trình qua vùng đất Điều khiển Thread trong Java, khám phá từng phương thức như những người phiêu lưu trong một thế giới mới. Nhớ rằng, giống như học nấu ăn, việc thành thạo điều khiển thread cần phải luyện tập. Đừng ngại thử nghiệm các phương thức này trong mã của riêng bạn.

Khi chúng ta kết thúc bài học này, tôi nhớ lại một sinh viên đã từng nói với tôi rằng việc hiểu thread cuối cùng đã "click" với cô ấy khi cô ấy tưởng tượng chúng như những đầu bếp khác nhau trong bếp, tất cả cùng nhau tạo ra một bữa ăn. Vậy, hãy tìm cho mình một ví dụ phù hợp với bạn!

Tiếp tục lập trình, tiếp tục học hỏi, và quan trọng nhất, hãy vui vẻ! Ai biết được, ứng dụng đa luồng lớn tiếp theo có thể đến từ một trong số các bạn. Đến gặp lại, chúc các bạn các thread chạy mượt mà và mã của bạn không có lỗi!

Credits: Image by storyset