zoukankan      html  css  js  c++  java
  • 线程的等待与唤醒

    概述

    1.wait、notify介绍,与锁的关系;

    2.wait、notify、notifyAll的使用;

    3.生产者消费者通过wait、notify来实现

    wait、notify介绍,与锁的关系

    1.wait、notify、notifyAll不属于Thread类,而是属于object类,也就是说每个对象都有这几种方法。那为什么要放到object类? 因为每个对象都有锁,而wait、notify、notifyAll是基于对象锁的。

    wait会使当前线程进入等待状态,直到其他线程调用此对象的notify或notifyAll方法,或被其他线程中断。调用wait的线程必须持有对象锁。

    notify 唤醒在此对象监视器上的单个线程,如果有多个线程则随机唤醒一个。直到当前线程释放锁,才能执行被唤醒的线程。notify也只能被持有对象锁的线程调用。

    notifyAll也是一样,不同的是notifyAll会唤醒此对象监视器上的所有线程。

    只能被持有对象锁的线程调用意味着wait/notify/notifyAll必须和synchronized方法/代码块中使用,而synchronized代码块内必须要有锁。

    可以试一下,在一个非synchronized代码块中调用wait/notify会出现什么问题?

    以下两个例子分别为在非synchronized代码块中调用和在synchronized代码块中调用但是未持有对象锁

    public class WaitNotify {
    
        public static void main(String[] args) throws InterruptedException {
    
            Object obj = new Object();
            obj.wait();
            obj.notifyAll();
        }
    }
    
    报错:
    Exception in thread "main" java.lang.IllegalMonitorStateException
    public class WaitNotify {
    
        public static void main(String[] args) throws InterruptedException {
    
            Object obj = new Object();
            Object lock = new Object();
            synchronized (lock) {
                obj.wait();
                obj.notifyAll();
            }
        }
    }
    
    报错:
    Exception in thread "main" java.lang.IllegalMonitorStateException

    为什么必须获取该对象的锁?

    因为在没有锁的情况下,wait和notify会出现竞态,举个常用的生产者消费者的例子

    假设有如下情景:

    1.生产者生成->2.生成队列满了->3.生产者进入等待状态

    a.消费者消费一个->b.生产者队列刷新->c.唤醒生产者线程

    在多线程的情况下,可能会出现这样的顺序:1->a->b->c->3,这样无对象锁会造成的问题就是生产者还没进入等待状态,消费者就已经notify了,这样生产者会一直处在wait状态;

    所有必须通过对象锁来实现,即队列对象的锁。来保证同步

    wait、notify、notifyAll的使用

    直接上代码了,在注释里面说明

    public class RunnableTest implements Runnable {
        private Integer ticket;
        public RunnableTest(int ticket){
    
            this.ticket=ticket;
        }
        @Override
        public void run() {
    
            synchronized (ticket){ //对ticket对象进行synchronized
                try {
                    System.out.println(Thread.currentThread().getName()+" wait...");
                    ticket.wait();
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
            for(int i=0;i<10;i++){
    
                if(this.ticket>0){
    
                    System.out.println(Thread.currentThread().getName()+" sell "+this.ticket--);
    
                }
            }
        }
    
        public static void main(String[] args){
    
            RunnableTest runnableTest=new RunnableTest(5);
            Thread test1=new Thread(runnableTest);
            Thread test2=new Thread(runnableTest);
            Thread test3=new Thread(runnableTest);
            test1.start();
    
            test2.start();
            System.out.println("after test2");
    
            test3.start();
            //到这里,三个线程会进入等待状态
            try {
                Thread.sleep(1000); //这时主线程等待1s,主要担心三个线程可能有某个线程还未wait就调用了notify了,这样线程会一直等待。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            synchronized (runnableTest.ticket){
    
                    runnableTest.ticket.notify();  //唤醒线程,这时输出结果还有两个线程处于等待状态, 如果使用notifyAll就不会。
            }
    
        }
    }

    生产者消费者通过wait、notify来实现

    //生产者消费者类
    public class ProduceConsumerWaitNotify {
    
    
        public static void main(String[] args){
    
            QueueClass resource = new QueueClass();
            //生产者线程
            Producer p = new Producer(resource);
            //多个消费者
            Consumer c1 = new Consumer(resource);
            Consumer c2 = new Consumer(resource);
            Consumer c3 = new Consumer(resource);
    
            p.start();
            c1.start();
            c2.start();
            c3.start();
        }
    }
    
    class Producer extends Thread{
    
        QueueClass queueClass;
        public Producer(QueueClass queueClass){
    
            this.queueClass=queueClass;
        }
    
        @Override
        public void run() {
    
            while(true){
    
                queueClass.add();
                try {
                    Thread.sleep((long)(1000*Math.random()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Consumer extends Thread{
    
        QueueClass queueClass;
        public Consumer(QueueClass queueClass){
    
            this.queueClass=queueClass;
        }
    
        @Override
        public void run() {
    
            while(true){
    
                try {
                    Thread.sleep((long) (1000 * Math.random()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                queueClass.remove();
    
            }
        }
    }
    //队列实现,通过wait,notifyAll来实现
    public class QueueClass {
    
        private int size=10;
        private int count=0;
    
        public synchronized void add(){
    
            if(count<size){
    
                count++;
                System.out.println(Thread.currentThread().getName()+" produce,now is "+count);
                notifyAll(); //唤醒在QueueClass对象锁上的等待线程
            }else{
    
                try {
                    wait(); //生成队列满了,持有当前对象锁的线程进入等待
                    System.out.println(Thread.currentThread().getName()+" producer waiting...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public synchronized void remove(){
    
            if(count>0){
    
                count--;
                System.out.println(Thread.currentThread().getName()+" consumer,now is "+count);
                notifyAll();
            }else{
    
                try {
                    wait();
                    System.out.println(Thread.currentThread().getName()+" consumer waiting...");
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
  • 相关阅读:
    VS2008编写MFC程序--使用opencv2.4()
    November 02nd, 2017 Week 44th Thursday
    November 01st, 2017 Week 44th Wednesday
    October 31st, 2017 Week 44th Tuesday
    October 30th, 2017 Week 44th Monday
    October 29th, 2017 Week 44th Sunday
    October 28th, 2017 Week 43rd Saturday
    October 27th, 2017 Week 43rd Friday
    October 26th, 2017 Week 43rd Thursday
    October 25th, 2017 Week 43rd Wednesday
  • 原文地址:https://www.cnblogs.com/dpains/p/7490252.html
Copyright © 2011-2022 走看看