Hạn chế của phương thức stop()
Từ các phiên bản đầu của nền tảng Java, lớp java.lang.Thread cung cấp phương thức stop() nhằm buộc một luồng phải hủy bỏ ngay lập tức. Tuy nhiên, cơ chế này gây ra nhiều rủi ro nghiêm trọng về tính nhất quán dữ liệu và đã chính thức bị đánh dấu @Deprecated:
- Hủy bỏ đột ngột: Luồng bị chấm dứt mà không kịp giải phóng tài nguyên hoặc thực thi khối
finally. - Mất khóa (Monitor): Tất cả các synchronized block mà luồng đang nắm giữ sẽ bị đóng băng ngay lập tức, dẫn đến nguy cơ deadlock hoặc dữ liệu chia sẻ bị hỏng ở trạng thái trung gian.
- Không kiểm soát được: Nhà phát triển không có cơ hội ghi nhận trạng thái lỗi hoặc khôi phục ứng dụng trước khi luồng chết.
Do đó, stop() tuyệt đối không được khuyến nghị sử dụng trong kiến trúc nghiệp vụ hiện đại.
Cơ chế interrupt() và mô hình hợp tác
Phương thức interrupt() tuân thủ nguyên tắc cooperative cancellation. Khi được gọi, JVM chỉ thiết lập cờ ngắt (interrupt flag) trên đối tượng Thread mục tiêu và chờ đợi nhà lập trình xử lý tín hiệu này một cách mạch lạc. Điều này đảm bảo luồng có thể dọn dẹp tài nguyên, commit giao dịch hoặc cập nhật trạng thái trước khi kết thúc.
Bộ ba phương thức kiểm soát trạng thái ngắt
public void interrupt()
Gán cờ ngắt thành true cho đối tượng Thread đích.
public boolean isInterrupted()
Trả về trạng thái cờ ngắt của Thread gọi hàm, không làm thay đổi giá trị cờ.
public static boolean interrupted()
Kiểm tra cờ ngắt của Thread đang thực thi, đồng thời tự động reset cờ về false sau khi đọc (thường dùng trong các vòng lặp polling).
Triển khai minh họa thực tế
Đoạn mã dưới đây mô phỏng một quy trình xử lý tuần tự, áp dụng đồng thời hai cơ chế điều khiển để quan sát phản hồi của JVM:
package vn.dev.thread.core;
public class TaskExecutor implements Runnable {
private volatile int executionCycle = 0;
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
executionCycle++;
System.out.printf("%s: Đang xử lý vòng #%d%n",
Thread.currentThread().getName(), executionCycle);
try {
Thread.sleep(80);
} catch (InterruptedException e) {
// Khôi phục cờ ngắt nếu bắt buộc phải dừng
Thread.currentThread().interrupt();
break;
}
}
System.out.printf("%s: Hủy bỏ hoàn tất do phát hiện tín hiệu ngắt.%n",
Thread.currentThread().getName());
}
public int getExecutionCycle() {
return executionCycle;
}
}
package vn.dev.thread.core;
public class ControlStrategyTest {
public static void main(String[] args) throws InterruptedException {
Thread hardKillThread = new Thread(new TaskExecutor(), "Luong-HardKill");
Thread softCancelThread = new Thread(new TaskExecutor(), "Luong-SoftCancel");
hardKillThread.start();
softCancelThread.start();
Thread.sleep(250);
// Cơ chế hủy cứng
hardKillThread.stop();
System.out.println("---> Gọi stop() trên Luong-HardKill");
// Cơ chế hủy mềm
softCancelThread.interrupt();
System.out.println("---> Gọi interrupt() trên Luong-SoftCancel");
}
}
Phân tích kết quả thực thi
Khi chạy mã nguồn trên, luồng đầu ra thường hiển thị như sau:
Luong-HardKill: Đang xử lý vòng #1
Luong-SoftCancel: Đang xử lý vòng #1
Luong-HardKill: Đang xử lý vòng #2
Luong-SoftCancel: Đang xử lý vòng #2
---> Gọi stop() trên Luong-HardKill
---> Gọi interrupt() trên Luong-SoftCancel
Luong-SoftCancel: Hủy bỏ hoàn tất do phát hiện tín hiệu ngắt.
Quan sát rõ ràng rằng stop() khiến Luong-HardKill biến mất ngay lập tức mà không in ra thông điệp kết thúc. Ngược lại, Luong-SoftCancel vẫn duy trì vòng lặp, gặp InterruptedException khi sleep bị gián đoạn bởi cờ ngắt, bẫy bắt ngoại lệ khôi phục cờ và跳出 vòng lặp để thực thi đoạn log hoàn tất trước khi JVM thu gom tiến trình.