首页 > Java > java教程 > 正文

如何在Java中使用notify和notifyAll

P粉602998670
发布: 2025-09-22 19:40:01
原创
954人浏览过
notify()唤醒单个等待线程,notifyAll()唤醒所有等待线程;示例中通过synchronized、wait()与notifyAll()实现生产者-消费者模型,确保线程安全通信。

如何在java中使用notify和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()
登录后复制
来唤醒它。

立即学习Java免费学习笔记(深入)”;

notify()
登录后复制
vs
notifyAll()
登录后复制
:应该选择哪个?

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

为什么需要

synchronized
登录后复制
关键字?

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

如知AI笔记
如知AI笔记

如知笔记——支持markdown的在线笔记,支持ai智能写作、AI搜索,支持DeepseekR1满血大模型

如知AI笔记27
查看详情 如知AI笔记

如何避免死锁?

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

  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
登录后复制
可能更合适。

以上就是如何在Java中使用notify和notifyAll的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号