Tạo Luồng
(Create Thread)

Khi bạn muốn tạo và khởi động một luồng (thread) trong Java, bạn cần cung cấp mã sẽ chạy trong luồng đó. Có hai cách chính để thực hiện điều này:

1. Triển khai giao diện Runnable

Cách thứ nhất để tạo luồng là khai báo một lớp triển khai giao diện Runnable.

Lớp này sẽ cần cài đặt phương thức run() chứa mã thực thi cho luồng.

Sau đó, một đối tượng của lớp này có thể được tạo ra và truyền vào một đối tượng Thread. Luồng này sẽ được bắt đầu bằng cách gọi phương thức start() của Thread.

Ví dụ: HelloRunnable.java

// Tạo một lớp triển khai giao diện Runnable
 class HelloRunnable implements Runnable {

    // Phương thức run() chứa mã sẽ được thực thi trong luồng
    public void run() {
        System.out.println("Xin chào từ một luồng!");
    }

    // Phương thức main để khởi chạy ứng dụng
    public static void main(String args[]) {
        // Tạo một đối tượng Thread với đối tượng Runnable
        Thread thread = new Thread(new HelloRunnable());
        
        // Bắt đầu luồng mới
        thread.start();
    }
}

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

Xin chào từ một luồng!

2. Kế thừa từ lớp Thread

Cách thứ hai đơn giản hơn để tạo một luồng là khai báo một lớp kế thừa từ Thread.

Lớp con này cần ghi đè phương thức run() của lớp Thread để định nghĩa mã thực thi trong luồng.

Sau đó, bạn có thể tạo một đối tượng từ lớp con này và gọi phương thức start() để bắt đầu luồng.

Ví dụ: HelloThread.java

// Tạo một lớp kế thừa từ lớp Thread
 class HelloThread extends Thread {

    // Ghi đè phương thức run() để định nghĩa mã thực thi trong luồng
    @Override
    public void run() {
        System.out.println("Xin chào từ một luồng!");
    }

    // Phương thức main để khởi chạy ứng dụng
    public static void main(String args[]) {
        // Tạo một đối tượng từ lớp HelloThread
        HelloThread thread = new HelloThread();
        
        // Bắt đầu luồng mới
        thread.start();
    }
}

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

Xin chào từ một luồng!

Khởi động luồng (Thread)

Cả hai ví dụ đều sử dụng phương thức Thread.start() để bắt đầu luồng mới. Phương thức start() sẽ kích hoạt luồng và gọi phương thức run() trong luồng đó.

Lưu ý: Nếu bạn gọi phương thức run() trực tiếp mà không thông qua phương thức start(), phương thức này sẽ chạy trong luồng hiện tại, chứ không phải trong luồng mới.

Start một thread hai lần

Một luồng xử lý (thread) chỉ có thể được khởi động một lần duy nhất. Nếu bạn cố gắng khởi động nó lần thứ hai, một ngoại lệ IllegalThreadStateException sẽ được ném ra. Luồng sẽ chạy bình thường ở lần khởi động đầu tiên, nhưng sẽ gặp lỗi ở lần thứ hai.

Dưới đây là một ví dụ minh họa về việc một luồng xử lý (thread) chỉ có thể được khởi động một lần duy nhất. Nếu bạn cố gắng khởi động nó lần thứ hai, một ngoại lệ IllegalThreadStateException sẽ được ném ra:

Ví dụ: MyThread.java

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Luồng đang chạy...");
    }
}

public class Example {
    public static void main(String[] args) {
        // Tạo một đối tượng luồng mới
        MyThread myThread = new MyThread();

        // Khởi động luồng lần đầu tiên
        myThread.start();

        // Thử khởi động lại luồng lần thứ hai
        try {
            myThread.start();
        } catch (IllegalThreadStateException e) {
            System.out.println("Lỗi: Bạn không thể khởi động lại một luồng đã chạy. " +
                               "Ngoại lệ IllegalThreadStateException được ném ra.");
        }
    }
}

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

Lỗi: Bạn không thể khởi động lại một luồng đã chạy. Ngoại lệ IllegalThreadStateException được ném ra.
Luồng đang chạy...

Trong ví dụ trên, luồng được chạy bình thường trong lần khởi động đầu tiên, nhưng khi cố gắng khởi động lại lần thứ hai, chương trình sẽ gặp lỗi.

Lưu ý:

↳ Mỗi luồng có một tên riêng để xác định. Nhiều luồng có thể có cùng tên.

↳ Nếu một tên không được chỉ định khi tạo luồng, Java sẽ tự động tạo ra một tên mới cho nó.

↳ Trừ khi có ghi chú khác, việc truyền đối số null cho bất kỳ hàm tạo hoặc phương thức nào trong lớp này sẽ gây ra ngoại lệ NullPointerException.

Nên sử dụng cách nào?

Cách đầu tiên, sử dụng đối tượng Runnable, linh hoạt hơn vì đối tượng Runnable có thể kế thừa từ một lớp khác ngoài Thread.

Cách thứ hai dễ sử dụng hơn trong các ứng dụng đơn giản, nhưng bị giới hạn bởi việc lớp nhiệm vụ (task class) phải là con cháu của Thread.

Bài học này tập trung vào cách tiếp cận đầu tiên, tách riêng nhiệm vụ Runnable ra khỏi đối tượng Thread thực thi nhiệm vụ đó. Cách tiếp cận này không chỉ linh hoạt hơn mà còn có thể áp dụng vào các API quản lý luồng ở mức cao được đề cập sau này.

Lớp Thread định nghĩa một số phương thức hữu ích cho việc quản lý luồng. Bao gồm các phương thức tĩnh, cung cấp thông tin hoặc ảnh hưởng đến trạng thái của luồng gọi phương thức. Các phương thức khác được gọi từ các luồng khác liên quan đến việc quản lý luồng và đối tượng Thread. Chúng ta sẽ xem xét một số phương thức này trong các phần tiếp theo.

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.”