Ngắt Luồng
(Interrupt Thread)
Khi làm việc với các luồng trong Java, hiểu cách Ngắt một luồng là rất quan trọng để quản lý luồng hiệu quả. Ngắt một luồng cho phép bạn báo hiệu cho luồng rằng nó nên dừng công việc hiện tại và thực hiện hành động khác, thường là để kết thúc hoặc thay đổi hoạt động của nó. Hãy cùng tìm hiểu cách hoạt động của việc Ngắt luồng, bao gồm các phương thức được cung cấp bởi lớp Thread, và xem một ví dụ cụ thể.
Cách hoạt động của việc ngắt luồng (Interrupt Thread)
1. Luồng trong trạng thái ngủ hoặc chờ
↳ Trạng thái ngủ (Thread.sleep()): Khi một luồng đang ngủ, tức là đang tạm dừng thực thi trong một khoảng thời gian nhất định do phương thức Thread.sleep() gây ra, và nếu phương thức interrupt() được gọi trên luồng đó, nó sẽ ngay lập tức thoát khỏi trạng thái ngủ. Trong trường hợp này, một ngoại lệ InterruptedException sẽ được ném ra.
↳ Trạng thái chờ (Object.wait()): Tương tự, khi một luồng đang chờ (tức là đang đợi một điều kiện nào đó xảy ra bằng phương thức Object.wait()), việc gọi phương thức interrupt() cũng sẽ khiến luồng thoát khỏi trạng thái chờ ngay lập tức và ném ra một InterruptedException.
2. Luồng không ở trạng thái ngủ hoặc chờ
Nếu luồng đang hoạt động bình thường và không ở trạng thái ngủ hoặc chờ khi interrupt() được gọi, nó sẽ không bị gián đoạn ngay lập tức. Thay vào đó, phương thức interrupt() chỉ đặt cờ gián đoạn của luồng thành true. Luồng có thể kiểm tra cờ gián đoạn này thông qua các phương thức như Thread.interrupted() hoặc Thread.isInterrupted() để quyết định cách xử lý.
Các phương thức để ngắt một luồng
Lớp Thread cung cấp ba phương thức chính để Ngắt và kiểm tra trạng thái Ngắt của một luồng:
↳ void interrupt(): Phương thức này được sử dụng để Ngắt một luồng. Nó đặt cờ Ngắt và, nếu luồng đang ở trạng thái chặn (như ngủ hoặc chờ), sẽ khiến nó ném ra một InterruptedException.
↳ boolean isInterrupted(): Phương thức này kiểm tra xem luồng có bị Ngắt không, nhưng không xóa trạng thái gián đoạn như phương thức interrupted().
↳ static boolean interrupted(): Phương thức này kiểm tra xem luồng hiện tại có bị Ngắt không và xóa trạng thái Ngắt. Nó trả về true nếu luồng hiện tại đã bị Ngắt.
Ví dụ 1
Dưới đây là một ví dụ về cách ngắt một luồng đang ở trạng thái ngủ (sleep()):
Ví dụ: Example.java
public class Example implements Runnable {
@Override
public void run() {
String[] importantInfo = {
"Bước 1",
"Bước 2",
"Bước 3",
"Bước 4"
};
for (String info : importantInfo) {
try {
// Dừng lại 4 giây
Thread.sleep(4000);
} catch (InterruptedException e) {
// Xử lý sự gián đoạn: dừng việc xử lý
System.out.println("Luồng bị gián đoạn, dừng tác vụ.");
return; // Thoát khỏi phương thức run
}
// In thông điệp
System.out.println(info);
}
}
public static void main(String[] args) {
Thread thread = new Thread(new Example());
thread.start();
// Mô phỏng sự gián đoạn
try {
Thread.sleep(5000); // Cho phép một chút thời gian để tác vụ chạy
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // Gián đoạn luồng
}
}
Kết quả của chương trình là
Luồng bị gián đoạn, dừng tác vụ.
Trong ví dụ này, luồng Example bị ngắt khi nó đang ở trạng thái ngủ (sleep()). Điều này dẫn đến việc ném ra InterruptedException, luồng in ra thông điệp gián đoạn và dừng hoạt động.
Ví dụ 2
Dưới đây là một ví dụ về cách ngắt một luồng đang ở trạng thái chờ (wait()):
Ví dụ: Example.java
class SharedResource {
// Phương thức synchronized để thực hiện việc chờ
public synchronized void doWait() {
try {
System.out.println("Luồng đang chờ...");
wait(); // Đưa luồng vào trạng thái chờ
System.out.println("Luồng đã được đánh thức và tiếp tục.");
} catch (InterruptedException e) {
System.out.println("Luồng bị gián đoạn khi đang chờ.");
}
}
}
public class Example {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
// Tạo và bắt đầu luồng thực hiện chờ
Thread thread = new Thread(() -> {
resource.doWait();
});
thread.start();
// Mô phỏng sự gián đoạn
try {
Thread.sleep(2000); // Cho phép một chút thời gian để luồng vào trạng thái chờ
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // Gián đoạn luồng
}
}
Kết quả của chương trình là
Luồng bị gián đoạn khi đang chờ.
Trong ví dụ này, luồng đang ở trạng thái chờ (wait()) bị ngắt bởi lệnh interrupt(). Điều này dẫn đến việc luồng thoát khỏi trạng thái chờ, ném ra InterruptedException, và sau đó xử lý việc gián đoạn theo mã trong khối catch.
Ví dụ 3
Dưới đây là một ví dụ về cách sử dụng phương thức Thread.interrupted() để kiểm tra và quyết định cách xử lý khi luồng bị gián đoạn mà không đang ở trạng thái ngủ hoặc chờ:
Ví dụ: Example.java
public class Example implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
// Kiểm tra cờ gián đoạn sau mỗi lần lặp
if (Thread.interrupted()) {
System.out.println("Luồng bị gián đoạn! Dừng tác vụ.");
return; // Thoát khỏi phương thức run khi bị gián đoạn
}
System.out.println("Đang thực hiện tác vụ: " + i);
// Mô phỏng một công việc tiêu tốn thời gian
try {
Thread.sleep(1000); // Ngủ 1 giây mỗi lần lặp
} catch (InterruptedException e) {
System.out.println("Luồng bị gián đoạn khi đang ngủ!");
return; // Thoát khỏi phương thức run khi bị gián đoạn trong trạng thái ngủ
}
}
System.out.println("Hoàn thành tác vụ.");
}
public static void main(String[] args) {
Thread thread = new Thread(new Example());
thread.start();
// Mô phỏng sự gián đoạn sau 3 giây
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // Gọi interrupt() để đặt cờ gián đoạn của luồng
}
}
Kết quả của chương trình là
Đang thực hiện tác vụ: 2
Đang thực hiện tác vụ: 3
Luồng bị gián đoạn khi đang ngủ!
Trong ví dụ này, khi interrupt() được gọi, luồng không bị gián đoạn ngay lập tức nếu nó không ở trạng thái ngủ hoặc chờ. Thay vào đó, cờ gián đoạn được đặt thành true, và luồng có thể kiểm tra cờ này trong quá trình thực thi. Nếu cờ gián đoạn được phát hiện, luồng sẽ dừng hoạt động một cách an toàn.
Ví dụ 4
Dưới đây là một ví dụ về cách sử dụng phương thức Thread.isInterrupted() để kiểm tra và quyết định cách xử lý khi luồng bị gián đoạn mà không đang ở trạng thái ngủ hoặc chờ:
Ví dụ: Example.java
public class Example implements Runnable {
@Override
public void run() {
while (true) {
// Kiểm tra xem luồng có bị gián đoạn không
if (Thread.currentThread().isInterrupted()) {
System.out.println("Luồng bị gián đoạn, dừng tác vụ.");
break; // Thoát khỏi vòng lặp và kết thúc luồng
}
// Thực hiện một số tác vụ không bị gián đoạn
System.out.println("Đang thực hiện tác vụ...");
try {
// Giả lập thời gian xử lý tác vụ
Thread.sleep(1000);
} catch (InterruptedException e) {
// Nếu bị gián đoạn trong lúc ngủ, đặt lại cờ gián đoạn
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) {
Thread thread = new Thread(new Example());
thread.start();
// Mô phỏng sự gián đoạn sau 3 giây
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // Gián đoạn luồng
}
}
Kết quả của chương trình là
Đang thực hiện tác vụ...
Đang thực hiện tác vụ...
Luồng bị gián đoạn, dừng tác vụ.
Trong ví dụ này, sau khi luồng bị gián đoạn (do gọi thread.interrupt()), nó sẽ dừng việc xử lý khi phát hiện cờ gián đoạn được đặt.