多线程中的wait和notify

1、wait和notify

由于线程之间是抢占式执行的,所以线程之间的执行先后顺序难以预知。但实际上是希望合理的协调多个线程之间的执行先后顺序。

 完成这个协调工作,主要涉及到三个方法

*wait()/wait(long timeout);让当前线程进入等待状态。

*notify()/notifyAll();唤醒在当前对象上等待的线程。

注意:wait,notify,notifyAll都是Object的方法,而之前提到的join()(等待当前线程运行结束)方法是Thread类中的方法,

2、wait方法

wait做的事情:

*使当前执行的代码的线程进行等待。(把线程放到等待队列中) 

*释放当前锁

*满足一定条件就会被唤醒,重新尝试获取这个锁。

wait要搭配synchronized一起使用,脱离synchronized使用wait就会抛出异常。

wait结束等待的条件:

*其他线程调用该对象的notify()方法

*wait等待时间超时(wait提供一个带有timeout参数的版本,来指定等待时间)

*其他线程调用该等待线程的interrupted方法,导致wait抛出InterrputedException异常。

3、 notify方法

notify是唤醒等待线程的方法

*方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其他线程,对其发出通知notify(),并使它们重新获取该对象的对象锁。

*如果有多个线程等待,则有线程调度器随机挑选一个状态为wait的线程,(并没有“先来后到的原则”)。

*在notify()方法后,当前线程不会马上释放该对象锁,要等待到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。

4、代码示例:

/**
 * wait()和notify()方法
 */
public class Exe_02 {
    public static void main(String[] args) {
        //定义一个锁对象
        Object locker=new Object();
        //创建调用wait()方法的线程
        Thread t1=new Thread(() ->{
            while(true){
                System.out.println("wait之前");
                try {
                    synchronized (locker) {
                        locker.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("wait之后");
                System.out.println("==============");
            }
        },"t1");
        //创建调用notify方法线程
        Thread t2=new Thread(() ->{
            while(true){
                System.out.println("notify之前");
                //加入锁
                synchronized (locker) {
                    locker.notify();
                }
                System.out.println("notify之后");
                //休眠一会
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t2");
        //启动线程
        t1.start();
        t2.start();
    }
}

 执行逻辑分析:

 注意:

1、当一个线程调用了wait()之后,就释放掉当前持有的锁,等待被其他线程唤醒。

2、当另一个线程调用了notify()之后, 之前调用wait()的线程被唤醒后,需要重新竞争锁资源然后再从之前wait的位置向下执行自己的逻辑。

4.1、notify()和notifyAll()

notify方法只是随机唤醒某一个线程,这个线程来获取锁;

notifyAll可以一次性唤醒所有等待的线程,这些线程全部去竞争锁,谁先拿到谁先处理。 

 5、观察多线程环境下锁状态

代码示例:

import org.openjdk.jol.info.ClassLayout;

public class Exe_03 {
    //定义一些变量
    private int count;
    private long count1=200;
    private String hello="";
    //定义一个对象变量
    private textExe_03 text03=new textExe_03();

    public static void main(String[] args) throws InterruptedException {
        //创建一个对象的实例
        Object obj=new Object();
        //打印实例布局
        System.out.println("=====任意object对象布局,起初无锁状态");
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        System.out.println("延迟三秒开启偏向锁");
        //延迟3秒开启偏向锁
        Thread.sleep(3000);
        //创建本类的实例
        Exe_03 exe_03=new Exe_03();
        //打印实例布局,查看锁状态
        System.out.println("=====打印实例布局,查看锁状态");
        System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        //调用hashCode后,保存hashCode的值
        exe_03.hashCode();
        //观察现象
        System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        System.out.println("==============================");
        System.out.println("synchronized加锁");
        //加锁后观察锁信息
        synchronized(exe_03){
            System.out.println("第一层synchronized加锁后");
            System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            //锁重入,观察锁信息
            synchronized(exe_03){
                System.out.println("第二层synchronized加锁后");
                System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            }
            //释放里层的锁
            System.out.println("释放内层锁后");
            System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        }
        //释放所有锁之后
        System.out.println("=========释放所有锁========");
        System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        System.out.println("==============================");
        //强制执行垃圾回收
        System.gc();
        //观察GC计数
        System.out.println("+++++++调用GC后查看age的值");
        System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        //打印类布局,调用不同的方法查看
        System.out.println("+++++查看类布局");
        System.out.println(ClassLayout.parseClass(Exe_03.class).toPrintable());
        //打印类对象布局
        System.out.println("+++++查看类对象布局");
        System.out.println(ClassLayout.parseInstance(Exe_03.class).toPrintable());
        synchronized (Exe_03.class){
            //加锁后的类对象
            System.out.println("+++++对类对象加锁后,不同的对象获取锁,观察锁升级为thin lock");
            System.out.println(ClassLayout.parseInstance(Exe_03.class).toPrintable());
        }
        //释放锁之后的类对象
        System.out.println("+++++释放锁后");
        System.out.println(ClassLayout.parseInstance(Exe_03.class).toPrintable());
        System.out.println("+++++多个锁线程参与锁竞争,观察锁状态+++++");
        Thread t1=new Thread(() ->{
            synchronized(exe_03){
                System.out.println("++++++在线程A中参与锁竞争,观察锁状态++++++");
                System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            }
        });
        t1.start();
        Thread t2=new Thread(() ->{
            synchronized(exe_03){
                System.out.println("++++++在线程B中参与锁竞争,观察锁状态++++++");
                System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            }
        });
        t2.start();
        Thread t3=new Thread(() ->{
            synchronized(exe_03){
                System.out.println("++++++在线程C中参与锁竞争,观察锁状态++++++");
                System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            }
        });
        t3.start();
    }
}
class textExe_03{

}

 查看运行结果:

 

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Javawaitnotify多线程编程的两个重要方法,用于线程之间的协作和通信。 wait方法可以使当前线程进入等待状态,直到其他线程调用notifynotifyAll方法唤醒它。在调用wait方法时,当前线程会释放它所持有的锁,以便其他线程可以访问共享资源。 notify方法用于唤醒一个处于等待状态的线程,如果有多个线程在等待,则只会唤醒其一个线程。notifyAll方法则会唤醒所有处于等待状态的线程。 waitnotify方法必须在同步块使用,即在使用这两个方法的对象上获取锁。否则会抛出IllegalMonitorStateException异常。 使用waitnotify方法可以实现线程之间的协作和通信,例如生产者消费者模型。在生产者消费者模型,生产者线程生产数据并将其放入共享队列,消费者线程从队列取出数据并进行消费。当队列为空时,消费者线程需要等待生产者线程生产数据,此时可以使用wait方法使消费者线程进入等待状态。当生产者线程生产数据并将其放入队列时,可以使用notify方法唤醒处于等待状态的消费者线程。 ### 回答2: Java 多线程waitnotify 是两个非常重要的方法,它们可以帮助线程之间达成协作,实现复杂的操作。wait 方法用于让当前线程进入等待状态,直到其他线程通过 notify 方法通知它继续执行。notify 方法则用于唤醒一个等待状态的线程,使其继续执行。 wait 方法 wait 方法用于让当前线程进入等待状态,直到其他线程通过 notifynotifyAll 方法唤醒它。wait 方法需要在 synchronized 代码块使用,否则会抛出 IllegalMonitorStateException 异常。在进入等待状态之后,线程将释放锁,并且进入一个等待池,等待其他线程调用 notifynotifyAll 方法唤醒它。 notify 方法 notify 方法用于唤醒一个等待状态的线程,使其继续执行。notify 方法同样需要在 synchronized 代码块使用,否则同样会抛出 IllegalMonitorStateException 异常。当一个线程调用 notify 方法时,等待池的线程将会被唤醒,但是它们不能马上继续执行,必须等待当前线程释放锁。如果有多个线程在等待池notify 方法只会唤醒其一个线程,具体唤醒哪个线程是随机的。 notifyAll 方法 notifyAll 方法与 notify 方法类似,但是它会唤醒所有等待池的线程。notifyAll 方法同样需要在 synchronized 代码块使用。 使用 waitnotify 实现线程协作 waitnotify 方法可以用来实现线程之间的协作,例如生产者和消费者问题。假设我们有一个共享的队列,生产者向队列添加数据,消费者从队列取出数据。如果队列已经满了,生产者就需要等待消费者取走数据,如果队列是空的,消费者就需要等待生产者加入新数据。 在这个问题,我们可以使用 waitnotify 方法来实现线程之间的协作,代码如下: ``` public class Queue { private final List<Integer> items = new LinkedList<>(); private static final int MAX_SIZE = 10; public synchronized void produce(int item) throws InterruptedException { while (items.size() == MAX_SIZE) { wait(); } items.add(item); notify(); } public synchronized int consume() throws InterruptedException { while (items.isEmpty()) { wait(); } int item = items.remove(0); notify(); return item; } } public class Producer implements Runnable { private final Queue queue; public Producer(Queue queue) { this.queue = queue; } public void run() { for (int i = 0; i < 20; i++) { try { queue.produce(i); System.out.println("Produced: " + i); } catch (InterruptedException ex) { ex.printStackTrace(); } } } } public class Consumer implements Runnable { private final Queue queue; public Consumer(Queue queue) { this.queue = queue; } public void run() { for (int i = 0; i < 20; i++) { try { int item = queue.consume(); System.out.println("Consumed: " + item); } catch (InterruptedException ex) { ex.printStackTrace(); } } } } public class Main { public static void main(String[] args) throws InterruptedException { Queue queue = new Queue(); Thread producer = new Thread(new Producer(queue)); Thread consumer = new Thread(new Consumer(queue)); producer.start(); consumer.start(); producer.join(); consumer.join(); } } ``` 在这个示例代码,我们创建了一个 Queue 类,它有两个方法 produce 和 consume 用于生产和消费数据。在 produce 方法,我们使用 while 循环来等待队列不满,如果队列已经满了,就调用 wait 方法进入等待状态。在 consume 方法,我们使用 while 循环来等待队列不空,如果队列是空的,就调用 wait 方法进入等待状态。在生产新数据或者消费数据之后,我们都调用 notify 方法来唤醒等待池的线程。 最后,我们可以使用 Producer 和 Consumer 类来生产和消费数据,它们分别运行在不同的线程。在运行这个程序时,生产者将不断生产数据,消费者将不断消费数据,一直到数据生产完毕为止。在这个过程,生产者和消费者之间通过 waitnotify 方法实现了线程之间的协作。 ### 回答3: Java是一种支持多线程的编程语言,在多线程编程过程,一个线程可能需要等待另一个线程的某个条件满足后才能继续执行。Java提供了waitnotify来实现线程之间的协作。 wait:使当前线程进入等待状态,释放对象的锁,直到其他线程调用notifynotifyAll方法唤醒它。wait方法必须在持有对象锁的情况下调用,否则会抛出IllegalMonitorStateException异常。 notify:唤醒一个处于等待状态的线程,如果有多个线程等待,则只会唤醒其一个,具体唤醒哪个线程无法预测。 notifyAll:唤醒所有处于等待状态的线程。 waitnotify必须在同步代码块调用,并且针对同一个对象。waitnotify的调用顺序也非常重要,如果先调用了notify而没有等待线程,会导致唤醒失效。 在多线程编程waitnotify常常用于生产者和消费者模式的线程之间的通信,生产者线程在生产完毕后调用notify方法唤醒消费者线程来消费数据,消费者线程在消费完毕后调用wait方法等待下一个生产者线程的唤醒。 waitnotify的使用需要谨慎,如果使用不当,会导致死锁或线程饥饿等问题。同时,在Java SE 5之后,Java提供了更加高级的线程库,如ReentrantLock、Condition等,可以更加方便和安全地实现线程之间的协作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值