【Java并发系列02】Object的wait()、notify()、notifyAll()方法使用

一、前言

  对于并发编程而言,除了Thread以外,对Object对象的wati和notify对象也应该深入了解其用法,虽然知识点不多。

二、线程安全基本知识

  首先应该记住以下基本点,先背下来也无妨:

  • 同一时间一个锁只能被一个线程持有
  • 调用对象的wait()和notify()前必须持有它

三、wait()和notify()理解

3.1 wait()和notify()方法简介

  wait()和notify()都是Object的方法,可以认为任意一个Object都是一种资源(或者资源的一个代表),当多个线程对一个资源进行操作时,如果线程发现这个资源还没有准备好,它就可以在这个资源上进行等待,即调用这个资源的wait()方法,如果有另外的线程经过某些处理觉得这个资源可用了,会调用这个这个资源的notify()方法,告诉等待它的线程,这个资源可以用了。

  当然不使用wait()和notify()方法也是可以的,可以用while()死循环来判断,如下面的伪代码:

class Resource{
    static boolean canUse=false;
}

while(!Resource.canUse){
    //如果不可用,死循环在这里等待
}

//当资源可以使用后,就会跳出循环,往下执行

  这样做是可以,但是特别消耗CPU资源,所以建议用户使用wait()和notify()方法。

3.2 wait()和notify()的价值

  其实从单词意思来看就能看出来,wait就是等待,说明这个资源没有准备好,我要等,还有这一个wait(long timeout) ,表示我只能等待你这么长时间了,过时不候啊,而调用notify()的线程肯定就是对资源进行处理的,处理完进行通知。所以呢,它们就经常用在生产者和消费者模式中任何涉及等资源到来的情景都适合用这两个方法

3.3 为什么wait()和notify()必须和synchronized一起使用

  当不在synchronized同步块中使用wait()和notify()或者调用方法的对象不是synchronized的同步锁就会抛异常:

java.lang.IllegalMonitorStateException

  很多人会疑惑为什么必须持有这个对象的锁才能调用对象的wait()和notify()方法呢,我也有这个疑惑,而且我认为这么做是没有必要的。首先看下面的代码:

public class WaitTest{
    // 这是一个资源,模拟的Object
    final NoObjct resource=new NoObjct();
    public static void main(String[] args) throws Exception{
        WaitTest d=new WaitTest();
        d.test();
    }

    public void test() throws Exception{
        Runnable r=new Runnable(){
            public void run(){
                // 调用资源的模拟的wait方法,在方法内部使用synchronized
                resource.noWait();
                System.out.println("线程等待完,执行");
            }
        };
        Thread t=new Thread(r);
        t.start();
        Thread.sleep(2000);
        System.out.println("准备唤醒等待资源的线程");
        // 调用资源的模拟的notify方法,在方法内部使用synchronized
        resource.noNotify();
    }
}

// 因wait()和notify()是final方法,不能覆盖,所以模拟一个Object对象
class NoObjct{
    // 模拟wait方法
    public void noWait(){
        // 这个就相当于将synchronized放到wait方法内部
        synchronized(this){
            try{
                this.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    // 模拟notify方法
    public void noNotify(){
        // 这个就相当于将synchronized放到notify方法内部
        synchronized(this){
            this.notify();
        }
    }
}

  这是一个简单的wait()和notify()例子,wait等待,notify唤醒。如果忽略掉模拟的Object会发现代码简洁了许多,否则就要每次使用synchronized,如下代码:

public class WaitTest{
    // 这是一个资源,模拟的Object
    final Object resource=new Object();

    public static void main(String[] args) throws Exception{
        WaitTest d=new WaitTest();
        d.test();
    }

    public void test() throws Exception{
        Runnable r=new Runnable(){
            public void run(){
                // 必须使用synchronized
                try{
                    synchronized(resource){
                        resource.wait();
                    }
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("线程等待完,执行");
            }
        };
        Thread t=new Thread(r);
        t.start();

        Thread.sleep(2000);
        System.out.println("准备唤醒等待资源的线程");
        // 必须使用synchronized
        synchronized(resource){
            resource.notify();
        }
    }
}

  所以呢,我觉得wait()和notify()和synchronized一起没有什么意义,毕竟synchronized用来进行代码同步的,和线程之间唤醒没有什么关系(希望有读者能给我相反的意见并说服我)。但是既然这么规定了就必须要去遵守,即必须在synchronized中使用wait和notify,且调用方法的对象必须是同步对象。

四、何时使用wait()和notify()

  在上面已经说了这两个方法的其中一个价值就是用在生产者和消费者模式。但是通过使用它们来构建的生产者和消费者模型很低级而且复杂,完全可以使用BlockingQueue接口的实现类来构建。比如可以使用ArrayBlockingQueue,它既能保证线程安全又能实现阻塞效果,何乐而不为呢。

  除此之外就只有线程间休眠与唤醒了。

  这一篇看似和并发没什么关系,但是因为涉及到多线程还是捎带着讲了一下。

  未经许可禁止转载。

时间: 2024-08-09 02:03:00

【Java并发系列02】Object的wait()、notify()、notifyAll()方法使用的相关文章

java并发系列(二)-----线程之间的协作(wait、notify、join、CountDownLatch、CyclicBarrier)

在java中,线程之间的切换是由操作系统说了算的,操作系统会给每个线程分配一个时间片,在时间片到期之后,线程让出cpu资源,由其他线程一起抢夺,那么如果开发想自己去在一定程度上(因为没办法100%控制它)让线程之间互相协作.通信,有哪些方式呢? wait.notify.notifyAll 1.void wait( ) 导致当前的线程等待,直到其他线程调用此对象的notify( ) 方法或 notifyAll( ) 方法 2.void wait(long timeout) 导致当前的线程等待,直到

Java 集合系列 02 Collection架构

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 首先,我们对Collection进行说明.下面先看看Collection的一些框架类的关系图: Collection是一个接口,它主要的两个分支是:List 和 Set. List和Set都是接口,它们继承于Collection.L

Java并发系列[5]----ReentrantLock源码分析

在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可见性.在大多数情况下,这些机制都能很好地完成工作,但却无法实现一些更高级的功能,例如,无法中断一个正在等待获取锁的线程,无法实现限定时间的获取锁机制,无法实现非阻塞结构的加锁规则等.而这些更灵活的加锁机制通常都能够提供更好的活跃性或性能.因此,在Java5.0中增加了一种新的机制:Reentrant

java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括InputStream)

我们以ByteArrayInputStream,拉开对字节类型的“输入流”的学习序幕.本章,我们会先对ByteArrayInputStream进行介绍,然后深入了解一下它的源码,最后通过示例来掌握它的用法. 转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_02.html ByteArrayInputStream 介绍 ByteArrayInputStream 是字节数组输入流.它继承于InputStream.它包含一个内部缓冲区,该缓冲区包含从流

Java Object 对象上的wait(),notify(),notifyAll()方法理解

第一阶段理解(2017-7-27): Java 将wait(),notify(),notifyAll()方法放在Object对象上,也就是说任何一个对象都可以调用这个方法,这与”任何一个对象都有一个内置锁,可以用于线程同步“是照应的.因此,当某个线程要释放cpu争夺权,让自己进入等待状态时,调用 某个锁(对象)上的wait()方法,也就是让当前线程等待其它线程调用该锁上的notify()或notify()方法.线程间通过同步锁来实现等待与唤醒协作.简单例子: 1 package com.lyyc

Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析

学习Java并发编程不得不去了解一下java.util.concurrent这个包,这个包下面有许多我们经常用到的并发工具类,例如:ReentrantLock, CountDownLatch, CyclicBarrier, Semaphore等.而这些类的底层实现都依赖于AbstractQueuedSynchronizer这个类,由此可见这个类的重要性.所以在Java并发系列文章中我首先对AbstractQueuedSynchronizer这个类进行分析,由于这个类比较重要,而且代码比较长,为了

Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式

在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概念,主要讲了AQS的排队区是怎样实现的,什么是独占模式和共享模式以及如何理解结点的等待状态.理解并掌握这些内容是后续阅读AQS源码的关键,所以建议读者先看完我的上一篇文章再回过头来看这篇就比较容易理解.在本篇中会介绍在独占模式下结点是怎样进入同步队列排队的,以及离开同步队列之前会进行哪些操作.AQS为在独占模

Java多线程学习8:wait()和notify()/notifyAll()

轮询 线程本身是操作系统中独立的个体,但是线程与线程之间不是独立的个体,因为它们彼此之间要相互通信和协作. 想像一个场景,A线程做int型变量i的累加操作,B线程等待i到了10000就打印出i,怎么处理?一个办法就是,B线程while(i == 10000),这样两个线程之间就有了通信,B线程不断通过轮训来检测i == 10000这个条件. 这样可以实现我们的需求,但是也带来了问题:CPU把资源浪费了B线程的轮询操作上,因为while操作并不释放CPU资源,导致了CPU会一直在这个线程中做判断操

Java多线程_wait/notify/notifyAll方法

关于这三个方法,我们可以查询API得到下列解释: wait():导致当前的线程等待,直到其他线程调用此对象的notify( ) 方法或 notifyAll( ) 方法或者指定的事件用完 notify():唤醒在此对象监视器上等待的单个线程 notifyAll():唤醒在此对象监视器上等待的所有线程 我们需要注意的点(1)wait().notify/notifyAll() 方法是Object的本地final方法,无法被重写. (2)wait() 与 notify/notifyAll 方法必须在同步