线程的等待与唤醒

概述

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();
            }
        }
    }
}
原文地址:https://www.cnblogs.com/dpains/p/7490252.html