17370845950

如何在Java中使用notify和notifyAll
notify()唤醒单个等待线程,notifyAll()唤醒所有等待线程;示例中通过synchronized、wait()与notifyAll()实现生产者-消费者模型,确保线程安全通信。

在Java中,

notify()
notifyAll()
方法用于线程间的通信,它们唤醒等待对象监视器的线程。
notify()
唤醒一个等待该对象监视器的线程,而
notifyAll()
唤醒所有等待该对象监视器的线程。

解决方案

使用

notify()
notifyAll()
的关键在于正确使用
synchronized
关键字和
wait()
方法。以下是一个简单的示例,演示了如何使用它们:

class Message {
    private String msg;
    private boolean empty = true;

    public synchronized String read() {
        while (empty) {
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        empty = true;
        notifyAll(); // 通知生产者可以生产
        return msg;
    }

    public synchronized void write(String msg) {
        while (!empty) {
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        empty = false;
        this.msg = msg;
        notifyAll(); // 通知消费者可以消费
    }
}

class Reader implements Runnable {
    private Message msg;

    public Reader(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            String message = msg.read();
            System.out.println("Reader got: " + message);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

class Writer implements Runnable {
    private Message msg;

    public Writer(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            msg.write("Message " + i);
            System.out.println("Writer wrote: Message " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

public class NotifyExample {
    public static void main(String[] args) {
        Message msg = new Message();
        new Thread(new Reader(msg)).start();
        new Thread(new Writer(msg)).start();
    }
}

这段代码展示了一个简单的生产者-消费者模型。

Message
类充当共享资源,
Reader
线程从
Message
读取数据,
Writer
线程向
Message
写入数据。
synchronized
关键字确保对
read()
write()
方法的互斥访问。
wait()
方法使线程进入等待状态,直到另一个线程调用
notifyAll()
来唤醒它。

notify()
vs
notifyAll()
:应该选择哪个?

notify()
只唤醒一个等待线程,而
notifyAll()
唤醒所有等待线程。 选择哪个取决于具体情况。 如果只有一个线程需要被唤醒,使用
notify()
可能更有效率,因为它避免了不必要的线程唤醒和上下文切换。 然而,在更复杂的情况下,例如有多个线程在等待不同条件,使用
notifyAll()
通常更安全,可以避免线程饥饿问题。

为什么需要

synchronized
关键字?

synchronized
关键字是确保线程安全的关键。 它提供了互斥锁,确保一次只有一个线程可以访问被
synchronized
保护的代码块或方法。 在上面的例子中,
synchronized
关键字确保
read()
write()
方法不会同时被多个线程调用,从而避免了数据竞争和不一致性。

如何避免死锁?

死锁是指两个或多个线程互相等待对方释放资源,导致所有线程都无法继续执行的情况。 为了避免死锁,需要遵循一些最佳实践:

  1. 避免循环等待: 确保线程不会以循环的方式请求资源。
  2. 按固定顺序获取锁: 如果需要获取多个锁,始终按照相同的顺序获取它们。
  3. 使用超时机制: 在尝试获取锁时,设置一个超时时间。 如果在超时时间内无法获取锁,则放弃并释放已持有的锁。
  4. 锁的粒度要小: 尽量减少锁的持有时间,并只锁定必要的代码块。

wait()
,
notify()
,
notifyAll()
必须在
synchronized
块中使用吗?

是的,

wait()
,
notify()
,
notifyAll()
方法必须在
synchronized
块中使用。 这是因为这些方法是用于线程间通信的,它们需要访问对象的监视器锁。 如果在
synchronized
块之外调用这些方法,会抛出
IllegalMonitorStateException

除了

wait()
,
notify()
,
notifyAll()
,还有其他线程间通信的方式吗?

当然,Java提供了多种线程间通信的方式,除了

wait()
,
notify()
,
notifyAll()
之外,还有:

  1. ReentrantLock
    Condition
    ReentrantLock
    synchronized
    关键字的更灵活的替代方案,它提供了更多的功能,例如公平锁和可中断锁。
    Condition
    接口允许线程在特定条件满足时等待和唤醒。
  2. BlockingQueue
    BlockingQueue
    是一个线程安全的队列,它提供了阻塞的
    put()
    take()
    方法。 当队列为空时,
    take()
    方法会阻塞,直到队列中有元素可用。 当队列已满时,
    put()
    方法会阻塞,直到队列中有空间可用。
  3. CountDownLatch
    CountDownLatch
    允许一个或多个线程等待其他线程完成操作。 它维护一个计数器,当计数器达到零时,所有等待线程都会被释放。
  4. CyclicBarrier
    CyclicBarrier
    允许一组线程互相等待,直到所有线程都到达一个公共点。 当所有线程都到达屏障时,屏障会被重置,允许线程继续执行。
  5. Exchanger
    Exchanger
    允许两个线程交换数据。 每个线程都将自己的数据传递给
    Exchanger
    ,并接收来自另一个线程的数据。
  6. Semaphore
    Semaphore
    控制对共享资源的访问。 它维护一个计数器,表示可用资源的数量。 线程可以请求获取一个许可,如果许可可用,则计数器减1。 线程可以释放一个许可,使计数器加1。

选择哪种线程间通信方式取决于具体的需求。 对于简单的生产者-消费者模型,

wait()
,
notify()
,
notifyAll()
可能就足够了。 对于更复杂的情况,
ReentrantLock
Condition
BlockingQueue
可能更合适。