多线程编程中关于wait和notify关键字

本文介绍了Java中wait和notify方法在多线程协作中的作用,强调了它们用于控制线程执行顺序,避免线程饥饿,以及在synchronized块内的使用规则。同时提到了notifyAll和wait/sleep的区别。
摘要由CSDN通过智能技术生成

wait和notify

因为多线程的调度是随机的,很多时候希望多个线程能够按照咱们规定的顺序来执行,完成线程之间的配合工作,wait和notify就是一个用来协调线程顺序的重要工具.

这两个方法都是Object提供的方法.随便找个对象,都可以使用wait和notify

这个异常说明,当wait引起线程阻塞之后,可以使用 interrupt 方法,把线程给唤醒,可以打断当前的阻塞状态的.

我们运行程序过后就会发现,这里还是有报错

wait的执行逻辑

wait在执行的时候,会做三件事

1.解锁:Object.wait就会尝试针对Object对象解锁.

2.阻塞等待

3.当被其他线程唤醒之后(一般wait搭配notify进行唤醒),就会尝试重新加锁,加锁成功,wait执行完毕,继续往下执行其他逻辑.

wait要解锁,前提就是先能加上锁,锁都没加,就想解锁,这里就当然会报错了.

如何解决?

核心思路:先加锁,在synchronized里面再进行wait!

此时它的状态是waiting

这里的wait就会一直阻塞到其他线程进行 notify 了.

wait和notify主要的用途就是用来安排线程之间的执行顺序,其中一个最典型的场景,就是能有效避免"线程饿死/饥饿(也就是吃不到CPU的资源)"

假设现在有ABCD四个线程,A线程现在在占用锁,当A线程释放锁之后,此时所有线程一拥而上,都来尝试获取这个锁,此时一种情况是,ABCD四个线程一拥而上,都去尝试获取这把锁,此时第二次获取到锁的不是A线程,另一种情况就是,A线程刚刚将锁释放掉,但是A线程同样会参与到锁竞争当中,此时A线程近水楼台先得月,又将锁占据了,后一种情况就会导致BCD线程发生"线程饿死"这种情况

public class Demo15 {
    private static Object object=new Object();
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
                while(true){
                    synchronized (object){
                    while(true){
                        System.out.println("wait开始");
                        try {
                            object.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("wait结束");
                    }
                }
            }
        });
        t1.start();
        Thread t2=new Thread(()->{
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (object){
                        System.out.println("notify开始");
                    object.notify();
                    System.out.println("notify结束");
                }
            }
        });
        t2.start();
    }
}

几个注意事项:

1.要想让 notify 能够顺利唤醒 wait 就需要确保 wait 和 notify 都是使用同一个对象调用的.

2.wait 和 notify 都需要放到 synchronized 之内的.虽然 notify 不涉及"解锁操作"但是Java也强制要求notify要放到synchronized之中(系统的原生API中没有这个要求)

3.如果进行notify的时候,另一个线程并没有处于wait状态.此时notify相当于"空打一炮"不会有任何的副作用.

在此处对于我们的t1,t2线程来说,t2线程就是用来辅助t1线程,使用notify对线程进行一个统筹规划的作用.在实际开发中,有的时候需要有多个线程按照一定的顺序来完成某些工作,此时就可以安排一个线程统一负责规划程序(但是这样的代码写起来是比较复杂的,实际开发过程中往往也不会真的这么去写,可以使用其他的代码结构来代替这种写法.)

但是线程可能有多个

比如,可以有N个线程进行wait,一个线程负责notify.notify操作只会唤醒一个wait,具体是唤醒了哪个线程?是随机的!

notifyAll

notifyAll负责唤醒全部处于 waiting 中的线程(但是,我们不推荐使用这个,因为这个违背了wait的初心,我们使用wait是为了让线程具有一定的顺序,而这里如果使用notifyAll,就会使所有上锁的线程一起解锁,从而恢复混乱局面).

那么如果我们想唤醒某个指定的线程,那该怎么办呢?

可以让不同的线程,使用不同的对象来进行wait.想要唤醒谁,就可以使用对应的对象来notify.(也就是多搞几把锁)

wait和sleep之间的区别

sleep是有一个明确的时间的,到达时间,自然就会被唤醒.也能提前唤醒,使用interrupt就可以.

wait默认是一个死等,一直等到有其他线程的notify(这个是顺理成章的唤醒,唤醒之后该线程还需要继续工作,后续还会进入wait状态),wait也能够被interrupt提前唤醒(告知线程要结束了,接下来线程就要进入到收尾工作了).其实wait也有一个带有超时时间的版本的.(和join类似)

因此,协调多个线程之间的执行顺序,当然还是优先使用 wait notify 而不是 sleep.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值