java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > 多线程wait()和notify()方法

Java多线程wait()和notify()方法详细图解

作者:Killing Vibe

wait()和notify()是直接隶属于Object类,也就是说所有对象都拥有这一对方法,下面这篇文章主要给大家介绍了关于Java多线程wait()和notify()方法详细图解的相关资料,需要的朋友可以参考下

一、线程间等待与唤醒机制

wait()和notify()是Object类的方法,用于线程的等待与唤醒,必须搭配synchronized 锁来使用。

多线程并发的场景下,有时需要某些线程先执行,这些线程执行结束后其他线程再继续执行。

比如: 一个长跑比赛,裁判员要等跑步运动员冲线了才能宣判比赛结束,那裁判员线程就得等待所有的运动员线程运行结束后,再唤醒这个裁判线程。

二、等待方法wait()

wait 做的事情:

wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常.

wait 结束等待的条件:

注意事项:

  1. 调用wait()方法的前提是首先要获取该对象的锁(synchronize对象锁)
  2. 调用wait()方法会释放锁,本线程进入等待队列等待被唤醒,被唤醒后不是立即恢复执行,而是进入阻塞队列竞争锁

等待方法:

1.痴汉方法,死等,线程进入阻塞态(WAITING)直到有其他线程调用notify方法唤醒

2.等待一段时间,若在该时间内线程被唤醒,则继续执行,若超过相应时间还没有其他线程唤醒此线程,此线程不再等待,恢复执行。

调用wait方法之后:

三、唤醒方法notify()

notify 方法是唤醒等待的线程.

注意事项:

四、关于wait和notify内部等待问题(重要)

对于wait和notify方法,其实有一个阻塞队列也有一个等待队列

举个栗子:

现有如下定义的等待线程任务

private static class WaitTask implements Runnable {
        private Object lock;
        public WaitTask(Object lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "准备进入等待状态");
                // 此线程在等待lock对象的notify方法唤醒
                try {
                    lock.wait();
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + "等待结束,本线程继续执行");
            }
        }
    }

然后创建三个等待线程:

由于同一时间只有一个线程(随机调度)能获取到synchronized锁,所以会有两个线程没竞争到锁,从而进入了阻塞队列

这里假如t2先竞争到了锁,所以先会阻塞t1和t3:

又由于调用wait方法会释放锁,调用wait方法的线程t2就会进入等待队列,直到被notify唤醒或者超时自动唤醒。

然后此时lock对象已经被释放了,所以t1和t3 又可以去竞争这个锁了,就从阻塞队列里面竞争锁。

这里假如t3 竞争到了锁,阻塞队列只剩下t1:

然后t3运行到了wait方法,释放锁,然后进入等待队列

然后重复这些操作~~,最后t1,t2,t3 都进入了等待队列中,等待notify线程唤醒(这里假设notify要放在这些线程start后的好几秒后,因为notify线程也是和这些线程并发执行的,所以等待队列中的线程随时可能被唤醒

重点来了:

在等待队列中的线程,被notify唤醒之后,会直接回到阻塞队列去竞争锁!!!而不是直接唤醒~

举个栗子:

拿notifyAll()来举例,假如此时等待队列中有三个线程t1,t2,t3,那么调用notifyAll()会直接把它们三个直接从等待队列中进入到阻塞队列中:

然后再去竞争这个锁,去执行wait之后的代码~~

五、完整代码(仅供测试用)

private static class WaitTask implements Runnable {
        private Object lock;
        public WaitTask(Object lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "准备进入等待状态");
                // 此线程在等待lock对象的notify方法唤醒
                try {
                    lock.wait();
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + "等待结束,本线程继续执行");
            }
        }
    }
    private static class NotifyTask implements Runnable {
        private Object lock;
        public NotifyTask(Object lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println("准备唤醒");
                // 唤醒所有线程(随机)
                lock.notifyAll();
                System.out.println("唤醒结束");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Object lock2 = new Object();
        // 创建三个等待线程
        Thread t1 = new Thread(new WaitTask(lock),"t1");
        Thread t2 = new Thread(new WaitTask(lock),"t2");
        Thread t3 = new Thread(new WaitTask(lock),"t3");
       // 创建一个唤醒线程
        Thread notify = new Thread(new NotifyTask(lock2),"notify线程");
        t1.start();
        t2.start();
        t3.start();
        ;
        Thread.sleep(100);
        notify.start();
        // 当前正在执行的线程数
        Thread.sleep(2000);
        System.out.println(Thread.activeCount() - 1);
    }

六、wait和sleep方法的区别(面试题):

总结

以上就是多线程场景下wait和notify方法的详解和注意事项了,更多相关多线程wait()和notify()方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文