Luồng Daemon
(Daemon Thread)

Luồng daemon trong Java là luồng cung cấp dịch vụ cho các luồng người dùng. Sự sống của nó phụ thuộc vào các luồng người dùng, nghĩa là khi tất cả các luồng người dùng kết thúc, JVM sẽ tự động kết thúc luồng daemon. Có nhiều luồng daemon tự động chạy trong Java, chẳng hạn như bộ thu rác (garbage collector), finalizer, v.v. Bạn có thể xem tất cả các chi tiết bằng cách gõ jconsole trong lệnh dòng. Công cụ jconsole cung cấp thông tin về các lớp đã tải, mức sử dụng bộ nhớ, các luồng đang chạy, v.v.

Những điểm cần nhớ về luồng Daemon trong Java:

↳ Cung cấp dịch vụ cho các luồng người dùng: Luồng daemon thực hiện các tác vụ hỗ trợ nền cho các luồng người dùng và không có vai trò quan trọng trong hệ thống ngoài việc phục vụ các luồng người dùng.

↳ Phụ thuộc vào các luồng người dùng: Sự sống của luồng daemon phụ thuộc vào sự tồn tại của các luồng người dùng. Nếu không còn luồng người dùng nào, JVM sẽ kết thúc luồng daemon.

↳ Độ ưu tiên thấp: Luồng daemon thường có độ ưu tiên thấp hơn so với các luồng người dùng.

Tại sao JVM kết thúc luồng daemon nếu không còn luồng người dùng?

↳ Mục đích chính của luồng daemon là cung cấp dịch vụ cho các luồng người dùng trong các tác vụ hỗ trợ nền. Nếu không có luồng người dùng, JVM không cần phải duy trì luồng daemon nữa. Vì vậy, JVM kết thúc luồng daemon khi không còn luồng người dùng nào.

Lớp java.lang.Thread cung cấp hai phương thức chính liên quan đến luồng daemon

↳ void setDaemon(boolean on): Phương thức này được sử dụng để đánh dấu luồng hiện tại là luồng daemon hoặc không phải là luồng daemon. Nếu tham số on là true, luồng được đánh dấu là luồng daemon. Ngược lại, nếu on là false, luồng không phải là luồng daemon.

↳ boolean isDaemon(): Phương thức này trả về giá trị true nếu luồng hiện tại là luồng daemon, và false nếu không phải là luồng daemon.

Ví dụ 1

Dưới đây là một ví dụ minh họa cách sử dụng các phương thức liên quan đến luồng daemon trong lớp Thread. Trong ví dụ này, chúng ta sẽ tạo một luồng daemon và một luồng không phải daemon, sau đó kiểm tra trạng thái của chúng.

Ví dụ: Example.java

class DaemonThread extends Thread {
    @Override
    public void run() {
        // Luồng sẽ chạy liên tục và in ra thông báo mỗi giây
        while (true) {
            System.out.println(getName() + " đang chạy với trạng thái daemon: " + isDaemon());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Example {
    public static void main(String[] args) {
        // Tạo một luồng daemon
        DaemonThread daemonThread = new DaemonThread();
        daemonThread.setName("DaemonThread");
        daemonThread.setDaemon(true); // Đánh dấu luồng là daemon
        daemonThread.start();

        // Tạo một luồng không phải daemon
        DaemonThread nonDaemonThread = new DaemonThread();
        nonDaemonThread.setName("NonDaemonThread");
        nonDaemonThread.setDaemon(false); // Đánh dấu luồng không phải daemon
        nonDaemonThread.start();

        // Đợi một chút để các luồng thực thi
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Kiểm tra trạng thái của các luồng
        System.out.println("Luồng DaemonThread là daemon: " + daemonThread.isDaemon());
        System.out.println("Luồng NonDaemonThread là daemon: " + nonDaemonThread.isDaemon());

        // Kết thúc chương trình
        System.out.println("Kết thúc chương trình.");
    }
}

Kết quả của chương trình là

DaemonThread đang chạy với trạng thái daemon: true
NonDaemonThread đang chạy với trạng thái daemon: false
DaemonThread đang chạy với trạng thái daemon: true
NonDaemonThread đang chạy với trạng thái daemon: false
DaemonThread đang chạy với trạng thái daemon: true
NonDaemonThread đang chạy với trạng thái daemon: false
...
(Kết quả tiếp tục cho đến khi chương trình kết thúc)
Luồng DaemonThread là daemon: true
Luồng NonDaemonThread là daemon: false
Kết thúc chương trình.

Lưu ý:

↳ Luồng Daemon: Luồng daemon sẽ tự động kết thúc khi tất cả các luồng không phải daemon kết thúc. Trong ví dụ này, khi chương trình chính kết thúc sau 5 giây, luồng daemon sẽ dừng lại ngay lập tức.

↳ Luồng Không Phải Daemon: Luồng này sẽ tiếp tục chạy ngay cả khi chương trình chính kết thúc cho đến khi nó tự kết thúc.

Ví dụ 2

Trong Java, bạn chỉ có thể đánh dấu một luồng là luồng daemon hoặc không phải luồng daemon trước khi nó bắt đầu. Nếu bạn cố gắng thay đổi trạng thái của luồng sau khi nó đã được bắt đầu, Java sẽ ném ngoại lệ IllegalThreadStateException.

Ví dụ: Example.java

class TestDaemonThread extends Thread {
    @Override
    public void run() {
        System.out.println("Tên luồng: " + Thread.currentThread().getName());
        System.out.println("Luồng Daemon: " + Thread.currentThread().isDaemon());
        try {
            Thread.sleep(2000); // Giữ luồng hoạt động một chút để quan sát
        } catch (InterruptedException e) {
            System.out.println("Luồng bị gián đoạn.");
        }
    }

    public static void main(String[] args) {
        // Tạo hai luồng
        TestDaemonThread t1 = new TestDaemonThread();
        TestDaemonThread t2 = new TestDaemonThread();

        // Khởi động luồng t1
        t1.start();

        // Đợi một chút để đảm bảo luồng t1 đã bắt đầu
        try {
            Thread.sleep(100); // Đảm bảo t1 đã bắt đầu
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Cố gắng thiết lập trạng thái daemon cho t1 sau khi đã khởi động
        try {
            t1.setDaemon(true); // Đây sẽ gây ra lỗi IllegalThreadStateException
        } catch (IllegalThreadStateException e) {
            System.out.println("Ngoại lệ: " + e);
        }

        // Khởi động luồng t2
        t2.start();

        // Đợi một chút để các luồng thực thi
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Kết thúc chương trình.");
    }
}

Kết quả của chương trình là

Tên luồng: Thread-0
Luồng Daemon: false
Ngoại lệ: java.lang.IllegalThreadStateException
Tên luồng: Thread-1
Luồng Daemon: false
Kết thúc chương trình.

Lưu ý: Thông báo lỗi có thể khác nhau tùy vào môi trường JVM của bạn. Thông thường, thông báo sẽ nêu rõ rằng việc thay đổi trạng thái của luồng sau khi nó đã bắt đầu là không hợp lệ.

Câu Nói Truyền Cảm Hứng

“Bắt đầu ở đâu không quan trọng, quan trọng là bạn sẵn sàng bắt đầu.” – W. Clement Stone

Không Gian Tích Cực

“Chúc bạn luôn giữ vững niềm tin và sức mạnh để vượt qua mọi thử thách trong cuộc sống.”