多线程的通知机制

典型应用场景–阻塞队列

生产者(Productor)—放入元素
消费者(Consumer)—取出元素
生产者放元素时,队列满了,该怎么办?
消费者取元素时,队列空了,该怎么办?
没有采用阻塞队列之前采用一种轮询模式,定期去问(性能浪费。实时性低)。
因此出现了另一种模式,通知模式,留个联系方式,状态改变以后会有人找你。

java中如何提供通知模式的?

Object的三个普通方法:
wait();导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。
notify();唤醒正在等待对象监视器的单个线程。
notifyAll();唤醒正在等待对象监视器的所有线程。

由于Obejct是java中所有类的祖先类,所以,所有类的对象,都具备这三个方法。

注意事项:

  1. 要在某个对象(o)上进行wait/notify/notifyAll,首先必须持有这个对象的锁
    在这里插入图片描述


import java.util.concurrent.TimeUnit;

class MyThread extends Thread{

    //    boolean condation = true;
    @Override
    public void run() {

            try {

                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();

            }
            synchronized (ThreadDemo.o){
                ThreadDemo.o.notify();
            }

    }
}
public class ThreadDemo {

    static Object o = new Object();

    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();

        System.out.println("准备开始wait");
        synchronized (o){
            o.wait();
        }
        System.out.println("从wait中醒来");
    }
}

  1. 先notify再wait可以收到之前的通知信号吗?
    无法收到,通知信号状态不保留。
  2. 在调用wait的线程真正放弃CPU之前隐含着一次释放锁的过程,释放o这个锁,而不是所有的锁。
    从wait返回之前,隐含着需要重新请求锁(o这个锁)。
    在这里插入图片描述
package 阻塞队列.demo2;
/*
通过jconsole查看线程持有锁的状态
*/

import java.util.*;
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Scanner s = new Scanner(System.in);
        synchronized (s){
            s.nextLine();
            System.out.println(1);//等待输入,不输入没有任何返回
            s.wait(20_0000);
            System.out.println(2);
            s.nextLine();
            System.out.println(3);
        }
    }
}

Thread.sleep(5000)和o.wait(5000)的区别

根本区别:
语义:
sleep休眠5秒(保证一定能够休眠5秒)
wait等待通知,最多等5秒(不保证一定能休眠够5秒,一旦被通知了,可以立即醒来)
表象区别:
sleep的调用不需要持有锁
wait休眠过程中,会释放锁,但只会释放o这个对象锁,其他持有的锁不释放。

package 阻塞队列.demo2;


import java.util.*;
class Lock1{

}
class Lock2{

}
public class ThreadDemo2 {
    public static void main(String[] args) throws InterruptedException {
        Object lock1 = new Lock1();
        Object lock2 = new Lock2();
        Scanner s = new Scanner(System.in);
        synchronized (lock1){
            synchronized (lock2){
                s.nextLine();  //lock1 & lock2
                System.out.println(1);
//                lock2.wait(5_0000);//lock1
                Thread.sleep(5_0000);//lock1 & lock2
                System.out.println(2);
                s.nextLine(); //lock1 & lock2
            }
        }

    }
}

wait被唤醒的条件

  1. 有其他线程调用notify,并且wait的线程被选中唤醒(随机唤醒)
  2. 有其他线程调用notifyAll
  3. 有其他线程执行类似interrupt()操作,通知线程停止,并抛出InterruptedException
  4. wait(timeout)达到超时时间时也会被唤醒
  5. 在某些系统上,由于JVM实现的原因,可能会出现虚假唤醒(上述的四个条件都没有发生的情况下)

一般调用wait方法,我们总是期待着某些条件。
所以一般的编码规范都是把wait放入一个循环中,循环的条件就是对我们的期望条件进行判断。
从wait中醒来时,无法保证条件符合我们的预期了。



import java.util.concurrent.TimeUnit;

class MyThread extends Thread {
    @Override
    public void run() {
        // 模拟算的时间比较久
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException exc) {
            exc.printStackTrace();
        }

        synchronized (ThreadDemo.class) {
            ThreadDemo.result = 100;
            ThreadDemo.class.notify();
        }
    }
}

public class ThreadDemo {
    static long result = -1;

    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();

        long r;
        // 等待子线程算完
        synchronized (ThreadDemo.class) {
            ThreadDemo.class.wait();
            r = result;
        }
        System.out.println(r); // 100
    }
}

leetcode1114 按序打印

保证操作都加锁并且加的是同一把锁


public class Foo {

private int step = 1;

    public Foo() {

    }

    public void first(Runnable printFirst) throws InterruptedException {


        // printFirst.run() outputs "first". Do not change or remove this line.
        while(true){
            synchronized (this){
                if (step == 1){
                    break;
                }
            }
            Thread.yield();
        }
        wait();


        printFirst.run();
        synchronized (this){
            step = 2;
        }


    }

    public void second(Runnable printSecond) throws InterruptedException {

        // printSecond.run() outputs "second". Do not change or remove this line.
        while(true){
            synchronized (this){
                if (step == 2){
                    break;
                }
            }
            Thread.yield();
        }


        printSecond.run();
        synchronized (this){
            step = 3;
        }

    }

    public void third(Runnable printThird) throws InterruptedException {

        // printThird.run() outputs "third". Do not change or remove this line.
        while(true){
            synchronized (this){
                if (step == 3){
                    break;
                }
            }
            Thread.yield();
        }


        printThird.run();
        synchronized (this){
            step = 4;
        }

    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值