Trang chủHướng dẫnCách sử dụng wait, notify, notifyAll trong Java thread (Có ví dụ minh họa)
Java

Cách sử dụng wait, notify, notifyAll trong Java thread (Có ví dụ minh họa)

CyStack blog 4 phút để đọc
CyStack blog10/06/2025
Locker Avatar

Bao Tran

Web Developer

Locker logo social
Reading Time: 4 minutes

Lớp Object trong Java có ba phương thức final cho phép các thread giao tiếp với nhau về trạng thái khóa (lock) của một tài nguyên. Các phương thức này là wait(), notify()notifyAll(). Hôm nay, chúng ta sẽ cùng tìm hiểu về chúng để nắm vững cách quản lý đồng bộ hóa giữa các luồng một cách hiệu quả.

Giao tiếp giữa các thread trong Java

wait(), notify() và notifyAll() trong Java

Chú ý: thread gọi các phương thức này trên một đối tượng bất kỳ cần phải nắm giữ monitor (cơ chế khóa và đồng bộ hóa) của đối tượng đó, nếu không sẽ gây ra lỗi java.lang.IllegalMonitorStateException.

wait()

Phương thức wait() của lớp Object có ba phiên bản. Một phiên bản sẽ khiến thread hiện tại đợi vô thời hạn cho đến khi một thread khác gọi phương thức notify() hoặc notifyAll() trên cùng đối tượng đó để đánh thức nó. Hai phiên bản còn lại đưa thread hiện tại vào trạng thái đợi trong một khoảng thời gian cụ thể trước khi nó tự động thức dậy.

notify()

Phương thức notify() chỉ đánh thức một thread duy nhất đang đợi trên đối tượng, và thread đó sẽ bắt đầu chạy. Do đó, nếu có nhiều thread cùng đang đợi trên một đối tượng, phương thức này sẽ chỉ đánh thức một thread trong số đó. Việc thread nào được chọn để đánh thức sẽ phụ thuộc vào cách hệ điều hành (OS) triển khai cơ chế quản lý thread.

notifyAll()

Phương thức notifyAll() đánh thức tất cả các thread đang đợi trên đối tượng. Tuy nhiên, thread nào được ưu tiên xử lý trước sẽ phụ thuộc vào hệ điều hành.

Các phương thức này có thể được dùng để triển khai bài toán kinh điển producer-consumer (sản xuất – tiêu thụ). Trong đó các consumer thread (thread tiêu thụ) đợi các đối tượng trong một Queue (hàng đợi), còn các producer thread (thread sản xuất) thì đưa đối tượng vào Queue và thông báo cho các thread đang đợi.

Hãy cùng xem một ví dụ cụ thể có nhiều thread cùng làm việc trên một đối tượng.

Message

Đây là một lớp Java Bean mà các thread sẽ làm việc trên đó và gọi các phương thức wait()notify().

package com.journaldev.concurrency;

public class Message {
    private String msg;
    
    public Message(String str){
        this.msg=str;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String str) {
        this.msg=str;
    }

}

Waiter

Đây là một lớp mà thread của nó sẽ đợi các thread khác gọi phương thức notify() (hoặc notifyAll()) để hoàn tất quá trình xử lý.

Lưu ý rằng thread Waiter sở hữu monitor của đối tượng Message thông qua việc sử dụng khối synchronized.

package com.journaldev.concurrency;

public class Waiter implements Runnable{
    
    private Message msg;
    
    public Waiter(Message m){
        this.msg=m;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        synchronized (msg) {
            try{
                System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());
                msg.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());
            //xử lý message
            System.out.println(name+" processed: "+msg.getMsg());
        }
    }

}

Notifier

Lớp này sẽ thực hiện xử lý trên đối tượng Message, sau đó gọi phương thức notify() để đánh thức các thread đang đợi đối tượng Message đó. Lưu ý rằng khối synchronized cũng được sử dụng để thread Notifier có thể sở hữu monitor của đối tượng Message.

package com.journaldev.concurrency;

public class Notifier implements Runnable {

    private Message msg;
    
    public Notifier(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name+" started");
        try {
            Thread.sleep(1000);
            synchronized (msg) {
                msg.setMsg(name+" Notifier work done");
                msg.notify();
                // msg.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }

}

WaitNotifyTest

Đây là lớp kiểm tra dùng để tạo ra nhiều thread WaiterNotifier, sau đó khởi chạy chúng.

package com.journaldev.concurrency;

public class WaitNotifyTest {

    public static void main(String[] args) {
        Message msg = new Message("process it");
        Waiter waiter = new Waiter(msg);
        new Thread(waiter,"waiter").start();
        
        Waiter waiter1 = new Waiter(msg);
        new Thread(waiter1, "waiter1").start();
        
        Notifier notifier = new Notifier(msg);
        new Thread(notifier, "notifier").start();
        System.out.println("All the threads are started");
    }

}

Khi ta chạy chương trình trên, ta sẽ quan sát thấy chương trình không kết thúc. Lý do là vì có hai thread đang đợi trên đối tượng Message, trong khi phương thức notify() chỉ đánh thức một thread, nên thread còn lại vẫn tiếp tục đợi tín hiệu thông báo.

waiter waiting to get notified at time:1356318734009
waiter1 waiting to get notified at time:1356318734010
All the threads are started
notifier started
waiter waiter thread got notified at time:1356318735011
waiter processed: notifier Notifier work done

Nếu ta comment lệnh gọi notify() và bỏ comment lệnh gọi notifyAll() trong lớp Notifier, kết quả output sẽ cho thấy chương trình chạy thành công và kết thúc.

waiter waiting to get notified at time:1356318917118
waiter1 waiting to get notified at time:1356318917118
All the threads are started
notifier started
waiter1 waiter thread got notified at time:1356318918120
waiter1 processed: notifier Notifier work done
waiter waiter thread got notified at time:1356318918120
waiter processed: notifier Notifier work done

Do phương thức notifyAll() đánh thức cả hai thread Waiter, chương trình sẽ hoàn tất và tự kết thúc sau khi thực thi xong.

Tổng kết

Trên đây là những điểm chính về wait(), notify()notifyAll() trong Java. Hãy nhớ nắm vững cách dùng chúng vào việc quản lý trạng thái và tương tác giữa các luồng để tránh các vấn đề như tranh chấp dữ liệu và deadlock trong ứng dụng đa luồng của bạn.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.