多线程wait和notify、notifyAll

多线程 wait和notify、notifyAll

  • wait 线程等待,释放本身占有的锁,然后进入锁的等待队列中等待唤醒
  • notify 唤醒一个处于等待队列中对希望获取自己占有的锁的线程
  • notifyAll 唤醒所有处于等待队列中对希望获取自己占有的锁的线程

wait

当一个线程调用wait()方法后,它会进入等待(WAITING)状态。这个状态是线程生命周期中的一种,表示线程正在等待某个条件成立或某个特定通知。

当线程调用某个对象的wait()方法时,它会做以下几件事:

  • 释放锁:线程会释放当前持有的对象的监视器锁(monitor lock),这使得其他线程有机会获取该对象的锁并执行同步代码块或同步方法。
  • 进入等待队列:线程会进入该对象的等待队列(wait set)中等待。等待队列中包含了所有调用该对象wait()方法并正在等待的线程。
  • 等待通知:线程将保持等待状态,直到其他线程调用同一个对象的notify()或notifyAll()方法。这些方法会唤醒等待队列中的一个或多个线程。
  • 重新竞争锁:被唤醒的线程不会立即执行,而是会重新进入就绪状态,等待获取该对象的监视器锁。一旦线程获取到锁,它将从wait()方法返回,并继续执行后续的代码。

需要注意的是,如果线程在等待期间被中断(即其他线程调用了该线程的interrupt()方法),wait()方法会抛出一个InterruptedException异常,并清除线程的中断状态。在这种情况下,线程需要自行处理中断异常,并决定是否继续等待或执行其他操作。

notify、notifyAll

当一个线程调用notify()或notifyAll()方法时,它不会改变自己的状态。这两个方法是由持有某个对象锁(也称为监视器锁)的线程调用的,用于唤醒正在等待(WAITING)在该对象上的其他线程。

具体地说:

  1. 调用线程:调用notify()或notifyAll()的线程会继续执行,并且仍然持有对象的锁。这意味着,在调用notify()或notifyAll()之后,调用线程会立即释放锁让其他线程获得锁的说法是不准确的。实际上,调用线程在退出同步块或同步方法时才会释放锁。
  2. 等待线程:被notify()或notifyAll()唤醒的线程会从对象的等待队列(wait set)中移到同步队列(entry set),等待获取对象的锁。一旦它们获得了锁,就会从wait()方法返回,并继续执行。
  3. 线程状态:
  • 调用notify()或notifyAll()的线程在调用这些方法时不会改变其状态,它将继续执行后续的代码。
  • 被唤醒的线程会从WAITING状态变为BLOCKED状态(如果它们尝试重新获取锁但锁不可用),或者变为RUNNABLE状态(如果它们成功获得了锁)。

简单地说,调用notify()或notifyAll()的线程本身不会进入任何特定的状态;它只是改变了其他等待线程的状态。

警告

  • waitnotifynotifyAll必须写在synchronized代码块或方法中
  • 生产者用notifynotifyAll,消费者用wait
  • waitnotifynotifyAll不要一起用,就是说一个方法内最好不要即用了 wait也用notifynotifyAll
  • 为了避免虚假唤醒,wait最好配合while一起使用

例子

package org.example.thread;

public class TDemo implements Runnable{

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("t2")) {
            for (int i = 0; i < 100; i++) {
                try {
                    increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }else {
            try {
                for (int i = 0; i < 100; i++) {
                    decrement();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    private int count = 0;

    public synchronized void increment() throws InterruptedException {
        count++;
        System.out.println("inc Count is: " + count);
        notifyAll(); // 唤醒可能在等待的线程
    }

    public synchronized void decrement() throws InterruptedException {
        while (count == 0) {
            wait(); // 如果count为0,则等待
        }
        count--;
        System.out.println("dec Count is: " + count);
    }
}



 public static void main(String[] args) throws ClassNotFoundException, InterruptedException, IOException {
        TDemo tDemo = new TDemo();
        Thread t1 = new Thread(tDemo,"t1");
        Thread t2 = new Thread(tDemo,"t2");
        t1.start();
        t2.start();
    }
posted @   勤匠  阅读(13)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示