多线程之wait()、notify()/notifyAll()使用,并模拟生产/消费者模式

要点总结:

1、wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写。

2、在当前线程拥有监视器锁的情况下使用,且wait()的对象和锁对象相同,否则将抛出异常IllegalMonitorStateException。一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。

3、wait() 使当前线程阻塞,并且会释放synchronized锁。被wait阻塞的线程,可以被notify/notifyAll()唤醒,重新获取锁后,可以从上一次wait()处继续往下执行。

4、wait() 可以被中断,需要处理中断异常。如果捕获但不处理,即:e.printStackTrace();将会抛出异常到jvm并结束异常线程;如果处理了异常,将会从catch{}后继续往下执行。("等待"状态被中断,就该就绪-运行状态了。)

5、notify() 可以唤醒一个受wait()阻塞的线程,唤醒哪一个,取决于系统,但被唤醒的线程还需要前提:能获取到synchronized锁才可以执行,因此当有多个线程wait住,使用notify() 可能出现死锁。

6、notifyAll() 可以唤醒全部受wait()阻塞的线程,被唤醒的线程还需要前提:能获取到synchronized锁才可以执行,因此notifyAll() 唤醒的线程并不是立刻都会被执行,能否执行、执行的顺序根据争抢锁的情况决定。

7、notify() /notifyAll() 可以唤醒受wait()阻塞的线程,但notify() 不会释放synchronized锁,只有synchronized锁住的代码块都执行完才会释放,所以 notify()/notifyAll() 一般写在代码块最后,唤醒后随即释放锁。

8、wait() 与notify()/notifyAll() 在运行时有顺序要求,如果A线程先执行notify()/notifyAll() 方法,B线程再执行wait方法,那么B线程是无法被唤醒的。所以使用notifyAll() 也可能出现死锁。

9、受wait()阻塞的线程被唤醒后,会从上一次wait()处继续往下执行。所以在利用共享变量进行条件判断时候,在阻塞与唤醒这期间,共享变量可能已经被其他线程修改过了,不一定再满足当前线程可执行条件,应该使用while循环判断而不是if。

wait状态的中断

 Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (obj) {
                  
              try {
                    System.out.println("我要一直等待被唤醒...");
                    obj.wait();
                    System.out.println("我被正常唤醒了...");
                } catch (InterruptedException e) {

                    System.out.println("我被打断了...");
                    // e.printStackTrace();
                }
                
                System.out.println("我继续执行了...");
            }
        }
    });

    thread.start();

    TimeUnit.SECONDS.sleep(1L); // 确保线程已经wait,再将他interrupt
    thread.interrupt();

输出:

我要一直等待被唤醒...
我被打断了...
我继续执行了...
 Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (obj) {
                
                while (list.size() <= 0) {
                    try {
                        System.out.println("我要一直等待被唤醒...");
                        obj.wait();
                        System.out.println("我被正常唤醒了...");
                    } catch (InterruptedException e) {

                        System.out.println("我被打断了...");
                        // e.printStackTrace();
                    }
                }

                System.out.println("我继续执行了...");
            }
        }
    });

    thread.start();

    TimeUnit.SECONDS.sleep(1L); // 确保线程已经wait,再将他interrupt
    thread.interrupt();

输出:

我要一直等待被唤醒...
我被打断了...
我要一直等待被唤醒... 
|              ----(被下一次循环wait住)

生产/消费者模式

// 生产者线程
Thread a = new Thread(new Runnable() {
    @Override
    public void run() {
        synchronized (obj) {

            while (list.size() >= 10) { // 有10个待消费的消息就暂停生产
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            list.add("A");
            System.out.println("生产者A 生产了一个消息....");
            obj.notifyAll();
        }
    }
}, "生产者A");


// 消费者线程
Thread c = new Thread(new Runnable() {
    @Override
    public void run() {

        while (true) { // 循环是让消费者可以一直收消息 --方便测试用
            synchronized (obj) {

                while (list.size() <= 0) {
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                String msg = list.removeFirst();
                System.out.println("消费者C 消费了一个消息...." + msg);
                obj.notifyAll();
            }
        }
    }
}, "消费者C");



c.start();

TimeUnit.SECONDS.sleep(1L);

a.start();

输出:

生产者A 生产了一个消息....
消费者C 消费了一个消息....A
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值