美文网首页Java 杂谈
并发编程之Wait和Notify

并发编程之Wait和Notify

作者: 西召 | 来源:发表于2019-03-24 19:05 被阅读1次

    Background

    相关概念

    什么是多线程

    我们把组成程序(Program)各个部分称为线程(Thread)。也可以说,线程就是程序中轻量级的进程(Process)。

    多线程(Multithreading)是Java的一个特性,它可以允许一个程序的多个部分(也就是线程)并发地执行,以达到最大程度利用CPU的目的。

    Multithreading is a Java feature that allows concurrent execution of two or more parts of a program for maximum utilization of CPU. Each part of such program is called a thread. So, threads are light-weight processes within a process.

    -- https://www.geeksforgeeks.org/multithreading-in-java/

    线程的状态

    线程的状态

    轮询

    Samples

    我们把循环执行某个逻辑判断,直到判断条件为true才执行判断体中的逻辑,叫做轮询(Polling)。轮询是会浪费一定的CPU资源的。

    The process of testing a condition repeatedly till it becomes true is known as polling.Polling is usually implemented with the help of loops to check whether a particular condition is true or not. If it is true, certain action is taken. This waste many CPU cycles and makes the implementation inefficient.

    -- https://www.geeksforgeeks.org/inter-thread-communication-java/

    下面提供一个轮询的实现示例。

    Message:

    isAvailable初始值是false,设置为true以后执行轮询体。

    注意要使用线程安全的AtomicBoolean,如果使用boolean,在多线程情况下会有意想不到的结果。

    import lombok.Getter;
    import lombok.Setter;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    @Setter
    @Getter
    public class Message {
        private AtomicBoolean isAvailable = new AtomicBoolean(false);
        private String msg;
        public Message(String str) {
            this.msg = str;
        }
    }
    

    PollingWaiter:

    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    
    public class PollingWaiter implements Runnable {
        private Message msg;
        public PollingWaiter(Message m) {
            this.msg = m;
        }
    
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            synchronized (msg) {
                int count = 0;
                System.out.println(name + " : waiter starting at time: " + LocalDateTime.now().format(DateTimeFormatter.ISO_TIME));
                while (!msg.getIsAvailable().get()) {
                    count++;
                }
                System.out.println(name + " : msg is available at time: " + LocalDateTime.now().format(DateTimeFormatter.ISO_TIME));
                System.out.println(name + " : msg is available after count: " + count);
                System.out.println(name + " : processed: " + msg.getMsg());
            }
        }
    }
    
    

    执行测试:

    休眠3秒以后,再执行轮询体内的代码。

    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class WaitNotifyTest {
    
        public static void main(String[] args) {
            testPolling();
        }
    
        public static void testPolling() {
    
            Message msg = new Message("process it");
    
            PollingWaiter waiter = new PollingWaiter(msg);
    
            new Thread(waiter, "PollingWaiter").start();
    
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            msg.setIsAvailable(new AtomicBoolean(true));
            System.out.println("over");
        }
    
    }
    

    输出结果:

    PollingWaiter : waiter starting at time: 14:26:08.482
    over
    PollingWaiter : msg is available at time: 14:26:11.402
    PollingWaiter : msg is available after count: -69547606
    PollingWaiter : processed: process it
    

    wait 和 notify

    除了轮询,Java通过wait 和 notify机制实现了线程间的通信。wait就是让执有某个对象的线程处于等待阻塞状态,而notify就是让等待阻塞中的线程重新获得CPU资源,再次进入运行状态。

    由于wait 和 notify相关的方法实现在了java.lang.Object类中,因此所有的子类都可以使用这些方法。

    wait 和 notify相关的方法需要在synchronized代码块中执行。

    wait 和 notify

    方法介绍

    下面简要介绍一下这些方法:

    • wait()

    wait()方法会导致当前线程从执行状态改为待执行状态,一直到另外一个线程为当前对象执行notify()或者notifyAll()方法。

    • wait(long timeout)

    wait()方法的不同点是,如果timeout时间到了以后,还没有前对象执行notify()或者notifyAll(),则线程自动开始执行。

    值得注意的是执行wait(0)wait()的效果是一样的。

    • wait(long timeout, int nanos)

    wait(long timeout)相比,此方法提供了等待超时设置的更高的精度,精确到了纳秒。

    1毫秒 = 1,000,000 纳秒。

    • notify()

    对于等待此对象的监视器的所有线程,执行notify()会随机唤醒一个线程。

    • notifyAll()

    相比与notify(),此方法会唤醒所有等待该对象的监视器的线程。

    示例

    在上面示例代码的基础上,增加如下代码实现。

    Waiter:

    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    
    public class Waiter implements Runnable{
    
        private Message msg;
    
        public Waiter(Message m){
            this.msg=m;
        }
    
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            synchronized (msg) {
                try{
                    System.out.println(name+" : waiting to get notified at time:"+ LocalDateTime.now().format(DateTimeFormatter.ISO_TIME));
                    msg.wait();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(name+" : waiter thread got notified at time:"+LocalDateTime.now().format(DateTimeFormatter.ISO_TIME));
                //process the message now
                System.out.println(name+" : processed: "+msg.getMsg());
            }
        }
    
    }
    

    Notifier:

    public class Notifier implements Runnable {
    
        private boolean isAll = true;
    
        private Message msg;
    
        public Notifier(Message msg, boolean isAll) {
            this.msg = msg;
            this.isAll = isAll;
        }
    
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            System.out.println(name + " started");
            try {
    
                Thread.sleep(3000);
    
                synchronized (msg) {
    
                    System.out.println(name + " : got the msg : "+msg.getMsg());
    
                    msg.setMsg(name + " : Notifier work done");
    
                    if (isAll) {
                        msg.notifyAll();
                    } else {
                        msg.notify();
                    }
    
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
    }
    

    WaitNotifyTest:

    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class WaitNotifyTest {
    
        public static void main(String[] args) {
            //testPolling();
            testNotify();
            //testNotifyAll();
        }
    
        public static void testPolling() {
    
            Message msg = new Message("process it");
    
            PollingWaiter waiter = new PollingWaiter(msg);
    
            new Thread(waiter, "PollingWaiter").start();
    
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            msg.setIsAvailable(new AtomicBoolean(true));
            System.out.println("over");
        }
    
        public static void testNotify() {
            Message msg = new Message("process it");
    
            Waiter waiter1 = new Waiter(msg);
            new Thread(waiter1, "waiter1").start();
    
            Waiter waiter2 = new Waiter(msg);
            new Thread(waiter2, "waiter2").start();
    
            Notifier notifier = new Notifier(msg, false);
            new Thread(notifier, "notifier").start();
    
            System.out.println("All the threads are started");
        }
    
        public static void testNotifyAll() {
            Message msg = new Message("process it");
    
            Waiter waiter1 = new Waiter(msg);
            new Thread(waiter1, "waiter1").start();
    
            Waiter waiter2 = new Waiter(msg);
            new Thread(waiter2, "waiter2").start();
    
            Notifier notifier = new Notifier(msg, false);
            new Thread(notifier, "notifier").start();
    
            System.out.println("All the threads are started");
        }
    }
    
    

    在启动两个线程同时执行wait方法的时候,会发现notify以后只有一个线程被唤醒了,而另一个线程则陷入了无尽地等待之中。

    Links

    仓库地址

    https://github.com/javastudydemo/jsd-concurrent/tree/master/jsd-concurrent/src/main/java/net/ijiangtao/tech/concurrent/jsd/waitnotify/demo1

    参考链接

    相关文章

      网友评论

        本文标题:并发编程之Wait和Notify

        本文链接:https://www.haomeiwen.com/subject/adxpvqtx.html