Home  >  Article  >  Java  >  How to use wait and notify to implement communication between threads in Java

How to use wait and notify to implement communication between threads in Java

WBOY
WBOYforward
2023-04-22 12:01:19680browse

    1. Why thread communication is needed

    Threads execute concurrently, which appears to be random execution of threads. However, in practical applications, we use thread communication The execution order is required, which requires the use of thread communication

    Thread communication Why not use priority to solve the running order of threads?

    The overall priority is determined by the priority information in the thread pcb and the thread waiting time, so in general development, priority will not be relied upon to indicate the execution order of threads

    See below Such a scene: a bakery example to describe the producer-consumer model

    There is a bakery with bakers and customers, corresponding to our producers and consumers, and the bakery has an inventory for To store bread, when the inventory is full, it will no longer be produced. At the same time, consumers are also buying bread. When the inventory of bread is sold out, consumers must wait for new bread to be produced before they can continue to buy

    Analysis: For what When to stop production and when to stop consumption, thread communication needs to be applied to accurately convey production and consumption information

    2. Wait and notify methods

    wait(): let the object held by the current thread Release the lock and wait

    wait(long timeout): The corresponding parameter is the time the thread waits

    notify(): Wake up the thread that uses the same object to call wait to enter the waiting thread, and compete for the object lock again

    notifyAll(): If there are multiple threads waiting, notifyAll wakes up all of them, notify wakes up one randomly

    Note:

    these The methods all belong to the methods in the Object class

    Must be used in synchronized synchronized code blocks/synchronized methods

    Which object is locked is the object to be used for wait and notify

    call It does not wake up immediately after notify, but waits until synchronized is completed before waking up

    1. wait() method

    After calling the wait method:

    Make the thread executing the current code wait (the thread is placed in the waiting queue)

    Release the current lock

    Be awakened when certain conditions are met, and try to acquire the lock again

    Conditions for the end of wait waiting:

    Other threads call the notify method of the object

    wait waiting time timeout (timeout parameter to specify the waiting time)

    Other threads Calling the interrupted method causes wait to throw InterruptedException

    2. notify() method

    When using the wait method without parameters, you need to use the notify method to wake up the thread and wait

    This method is to wake up those threads waiting for the object lock of the object so that they can reacquire the object lock of the object

    If there are multiple threads waiting, the thread scheduler will randomly select a wait Status thread (there is no first-come, first-served)

    After the notify() method, the current thread will not release the object lock immediately. It will wait until the thread executing the notify() method finishes executing the program, that is, exiting the synchronization code The object lock will be released only after the block

    3. notifyAll() method

    This method has the same effect as the notify() method, except that when waking up, all waiting threads will be awakened

    notify() method just randomly wakes up a thread

    3. Use wait and notify to implement bakery business

    Prerequisite:

    Yes 2 bakers, the baker can make two loaves at a time

    The warehouse can store 100 loaves

    There are 10 consumers, each consumer buys one loaf at a time

    Note:

    Consumption and production are carried out concurrently and in parallel, not one production and one consumption.

    Implementation code:

    public class Bakery {
        private static int total;//库存
     
        public static void main(String[] args) {
            Producer producer = new Producer();
            for(int i = 0;i < 2;i++){
                new Thread(producer,"面包师傅-"+(i-1)).start();
            }
            Consumer consumer = new Consumer();
            for(int i = 0;i < 10;i++){
                new Thread(consumer,"消费者-"+(i-1)).start();
            }
        }
        private static class Producer implements Runnable{
            private int num = 3; //生产者每次生产三个面包
            @Override
            public void run() {
                try {
                    while(true){ //一直生产
                        synchronized (Bakery.class){
                            while((total+num)>100){ //仓库满了,生产者等待
                                Bakery.class.wait();
                            }
                            //等待解除
                            total += num;
                            System.out.println(Thread.currentThread().getName()+"生产面包,库存:"+total);
                            Thread.sleep(500);
                            Bakery.class.notifyAll(); //唤醒生产
                        }
                        Thread.sleep(500);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        private static class Consumer implements Runnable{
            private int num = 1; //消费者每次消费1个面包
            @Override
            public void run() {
                try {
                    while(true){ //一直消费
                        synchronized (Bakery.class){
                            while((total-num)<0){ //仓库空了,消费者等待
                                Bakery.class.wait();
                            }
                            //解除消费者等待
                            total -= num;
                            System.out.println(Thread.currentThread().getName()+"消费面包,库存:"+total);
                            Thread.sleep(500);
                            Bakery.class.notifyAll(); //唤醒消费
                        }
                        Thread.sleep(500);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    Partial printing results:

    How to use wait and notify to implement communication between threads in Java

    4. Blocking queue

    The blocking queue is a special queue that also follows the "first in, first out" principle. It is a thread-safe queue structure

    Features: A typical producer-consumer model, generally used for task decoupling and peak reduction

    When the queue is full, the queue will be blocked and waited. (Production), until other threads take elements from the queue
    When the queue is empty, the queue will be blocked and wait (Consumption), until other threads insert elements into the queue

    1. Producer Consumer model

    The producer-consumer model uses a container to solve the problem of strong coupling between producers and consumers

    Producers and consumers do not communicate directly with each other, but through blocking The queue is used for communication, so after the producer produces the data, it waits for the consumer to process it and throws it directly to the blocking queue. The consumer does not ask the producer for data, but directly takes it from the blocking queue.

    The blocking queue is equivalent In a buffer, it balances the processing capabilities of producers and consumers
    Blocking queue can also decouple producers and consumers

    The implementation of the above bakery business is the producer-consumer model An example of

    2. Blocking queue in the standard library

    The blocking queue is built into the Java standard library. If we need to use the blocking queue in some programs, directly use the blocking queue in the standard library. That’s it

    BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue

    put 方法用于阻塞式的入队列, take 用于阻塞式的出队列

    BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性

            BlockingDeque queue = new LinkedBlockingDeque<>();
            queue.put("hello");
            //如果队列为空,直接出出队列就会阻塞
            String ret = queue.take();
            System.out.println(ret);

    3. 阻塞队列的模拟实现

    这里使用数组实现一个循环队列来模拟阻塞队列

    当队列为空的时候,就不能取元素了,就进入wait等待,当有元素存放时,唤醒

    当队列为满的时候,就不能存元素了,就进入wait等待,当铀元素取出时,唤醒 

    实现代码:

    public class MyBlockingQueue {
        //使用数组实现一个循环队列,队列里面存放的是线程要执行的任务
        private Runnable[] tasks;
        //队列中任务的数量,根据数量来判断是否可以存取
        private int count;
        private int putIndex; //存放任务位置
        private int takeIndex; //取出任务位置
     
        //有参的构造方法,表示队列容量
        public MyBlockingQueue(int size){
            tasks = new Runnable[size];
        }
     
        //存任务
        public void put(Runnable task){
            try {
                synchronized (MyBlockingQueue.class){
                    //如果队列容量满了,则存任务等待
                    while(count == tasks.length){
                        MyBlockingQueue.class.wait();
                    }
                    tasks[putIndex] = task; //将任务放入数组
                    putIndex = (putIndex+1) % tasks.length; //更新存任务位置
                    count++; //更新存放数量
                    MyBlockingQueue.class.notifyAll(); //唤醒存任务
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
     
        //取任务
        public Runnable take(){
            try {
                synchronized (MyBlockingQueue.class){
                    //如果队列任务为空,则取任务等待
                    while(count==0){
                        MyBlockingQueue.class.wait();
                    }
                    //取任务
                    Runnable task = tasks[takeIndex];
                    takeIndex = (takeIndex+1) % tasks.length; //更新取任务位置
                    count--; //更新存放数量
                    MyBlockingQueue.class.notifyAll(); //唤醒取任务
                    return task;
                }
            } catch (InterruptedException e) {
               throw new RuntimeException("存放任务出错",e);
            }
        }
    }

    五. wait和sleep的区别(面试题)

    相同点:

    都可以让线程放弃执行一段时间 

    不同点:

    ☘️wait用于线程通信,让线程在等待队列中等待

    ☘️sleep让线程阻塞一段时间,阻塞在阻塞队列中

    ☘️wait需要搭配synchronized使用,sleep不用搭配

    ☘️wait是Object类的方法,sleep是Thread的静态方法

    The above is the detailed content of How to use wait and notify to implement communication between threads in Java. For more information, please follow other related articles on the PHP Chinese website!

    Statement:
    This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete