漫画:Object类很大,你忍一下(完结篇)

简介: 这一次,我们来重点讲解 wait(),notify(),notifyAll() 这三大方法。


 

这一次,我们来重点讲解 wait()notify()notifyAll() 这三大方法。

 

640.png640.png640.png640.png640.png

 



// 执行这个方法后,持有此对象监视器的线程会进入等待队列,同时释放锁
// 如果不在synchronized修饰的方法或代码块里调用,则会抛出IllegalMonitorStateException 异常
// 如果当前线程在等待时被中断,则抛出InterruptedException异常
public final void wait() throws InterruptedException {
    wait(0);
}
// timeout是线程等待时间,时间结束则自动唤醒,单位ms
// Java默认的实现方式,native实现
public final native void wait(long timeout) throws InterruptedException;
// nanos是更精确的线程等待时间,单位ns(1 ms = 1,000,000 ns)
// Java默认的实现方式
public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                "nanosecond timeout value out of range");
    }
    if (nanos > 0) {
        timeout++;
    }
    wait(timeout);
}

 640.png640.png640.png

含参数的wait()方法调用以后,线程可以在等待时间结束后被唤醒;无参的wait()方法调用后,则必须等待持有该对象监视器的线程主动调用notify()notifyAll()方法后才能被唤醒。

两者之间的区别,在于notify()方法唤醒在此对象监视器上等待的单个线程,而notifyAll()方法则唤醒在此对象监视器上等待的所有线程。

wait方法一样,执行notify方法的线程也必须提前获取锁。需要注意的是,被notify方法唤醒的线程并不会立即执行,因为要等调用notify方法的线程释放锁之后才会获取到锁。

640.png640.png

 

有的Java虚拟机(VM)会选择最先调用wait方法的线程,也有的则会随机选择一个线程。尽管notify方法的处理速度比notifyAll方法更快,但使用notifyAll方法更为稳妥。

notifynotifyAll的方法声明如下:


 

public final native void notify();
public final native void notifyAll();

640.png640.png640.png640.png640.png

有一家公司开始想要招聘程序员,于是面试了几名候选人:

 

640.png

 

 

但公司录用程序员的时间是不确定的,需要综合考虑,无法给出及时反馈。于是告诉他们回去等通知。

 

640.png

 

经过事后的比较和研究,面试官觉得大黄是招聘的最佳人选,于是让HR小姐姐通知大黄,也就是唤醒了大黄:

640.png640.png640.png


1wait()notify()notifyAll()必须在synchronized修饰的方法或代码块中使用。

2)在while循环里而不是if语句下使用wait(),确保在线程睡眠前后都检查wait()触发的条件(防止虚假唤醒)。

3wait()方法必须在多线程共享的对象上调用。

 640.png640.png

 

生产者/消费者模型能解决绝大多数并发问题,通过平衡生产线程和消费线程的工作能力,来提高程序的整体处理数据的速度。

生产者线程和消费者线程的处理速度差异,会引起消费者想要获取数据时,数据还没生成或者生产者想要交付数据,却没有消费者接收的问题。对于这样的问题,生产者/消费者模型可以消除这个差异。

640.png640.png

 

首先,按照注意事项(1)和(2)的要求,定义一个生产者,往队列里添加元素:

// 生产者,有详细的注释
public class Producer implements Runnable{
    private Queue<Integer> queue;
    private int maxSize;
    public Producer(Queue<Integer> queue, int maxSize){
        this.queue = queue;
        this.maxSize = maxSize;
    }
    @Override
    public void run() {
        // 这里为了方便演示做了一个死循环,现实开发中不要这样搞
        while (true){
            //(1)wait()、notify()和notifyAll()必须在synchronized修饰的方法或代码块中使用
            synchronized (queue){
               //(2)在while循环里而不是if语句下使用wait(),确保在线程睡眠前后都检查wait()触发的条件(防止虚假唤醒)
                while (queue.size() == maxSize){
                    try{
                        System.out.println("Queue is Full");
                        // 生产者线程进入等待状态,在此对象监视器上等待的所有线程(其实只有那个消费者线程)开始争夺锁
                        queue.wait();
                    }catch (InterruptedException ie){
                        ie.printStackTrace();
                    }
                }
                Random random = new Random();
                int i = random.nextInt();
                System.out.println("Produce " + i);
                queue.add(i);
               // 唤醒这个Queue对象的等待池中的所有线程(其实只有那个消费者线程),等待获取对象监视器
                queue.notifyAll();
            }
        }
    }
}

 

接下来,再定义一个与之类似的消费者类,除了从队列里移除元素的逻辑之外,整体代码大同小异:

// 消费者类
public class Consumer implements Runnable{
    private Queue<Integer> queue;
    private int maxSize;
    public Consumer(Queue<Integer> queue, int maxSize){
        this.queue = queue;
        this.maxSize = maxSize;
    }
    @Override
    public void run() {
        while (true){
            synchronized (queue){
                while (queue.isEmpty()){
                    System.out.println("Queue is Empty");
                    try{
                        queue.wait();
                    }catch (InterruptedException ie){
                        ie.printStackTrace();
                    }
                }
                int v = queue.remove();
                System.out.println("Consume " + v);
                queue.notifyAll();
            }
        }
    }
}

 

最后编写符合注意事项(3)的测试代码:

public void test(){
    //(3)wait()方法必须在多线程共享的对象上调用
    // 这个队列就是给消费者、生产者两个线程共享的对象
    Queue<Integer> queue = new LinkedList<>();
    int maxSize = 5;
    Producer p = new Producer(queue, maxSize);
    Consumer c = new Consumer(queue, maxSize);
    Thread pT = new Thread(p);
    Thread pC = new Thread(c);
    // 生产者线程启动,获取锁
    pT.start();
    // 消费者线程启动
    pC.start();
}

最终的查看运行结果如下:

 

Produce 1604006010Produce 1312202442Produce -1478853208Produce 1460408111Produce 1802825495Queue is Full
Consume
1604006010Consume 1312202442Consume -1478853208Consume 1460408111Consume 1802825495Queue is Empty


 640.png640.png640.png640.png

相关文章
|
10天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
49 4
|
1月前
|
Java
Java Object 类详解
在 Java 中,`Object` 类是所有类的根类,每个 Java 类都直接或间接继承自 `Object`。作为所有类的超类,`Object` 定义了若干基本方法,如 `equals`、`hashCode`、`toString` 等,这些方法在所有对象中均可使用。通过重写这些方法,可以实现基于内容的比较、生成有意义的字符串表示以及确保哈希码的一致性。此外,`Object` 还提供了 `clone`、`getClass`、`notify`、`notifyAll` 和 `wait` 等方法,支持对象克隆、反射机制及线程同步。理解和重写这些方法有助于提升 Java 代码的可读性和可维护性。
|
3月前
|
Java
【Java基础面试二十】、介绍一下Object类中的方法
这篇文章介绍了Java中Object类的常用方法,包括`getClass()`、`equals()`、`hashCode()`、`toString()`、`wait()`、`notify()`、`notifyAll()`和`clone()`,并提到了不推荐使用的`finalize()`方法。
【Java基础面试二十】、介绍一下Object类中的方法
|
2月前
|
Python
类与面向对象编程(Object-Oriented Programming, OOP)
类与面向对象编程(Object-Oriented Programming, OOP)
|
3月前
|
前端开发 Java 编译器
【前端学java】java中的Object类和前端中的Object有什么区别(9)
【8月更文挑战第10天】java中的Object类和前端中的Object有什么区别
43 0
【前端学java】java中的Object类和前端中的Object有什么区别(9)
|
3月前
|
算法 Java
12 Java常用类(一)(内部类+object类+包装类)
12 Java常用类(一)(内部类+object类+包装类)
37 5
|
4月前
|
Java
Java中的Object类 ( 详解toString方法 | equals方法 )
Java中的Object类 ( 详解toString方法 | equals方法 )
|
5月前
|
Java
【Java】Object类简单解析
【Java】Object类简单解析
63 1
|
5月前
|
前端开发 JavaScript Java
Java基础10-深入理解Class类和Object类(二)
Java基础10-深入理解Class类和Object类(二)
48 5
|
5月前
|
Java C++
Java基础10-深入理解Class类和Object类(一)
Java基础10-深入理解Class类和Object类(一)
72 4