Java并发之线程间协作Object的wait()、notify()、notifyAll()

wait()、notify()和notifyAll()是Object类中的方法:

1)wait()、notify()和notifyAll()方法是本地方法,而且为final方法,无法被重写。 

2)调用某个对象的wait()方法能让当前线程堵塞。而且当前线程必须拥有此对象的monitor(即锁)

3)调用某个对象的notify()方法可以唤醒一个正在等待这个对象的monitor的线程,假设有多个线程都在等待这个对象的     monitor。则仅仅能唤醒当中一个线程;

4)调用notifyAll()方法可以唤醒全部正在等待这个对象的monitor的线程;

为何这三个不是Thread类声明中的方法。而是Object类中声明的方法(当然因为Thread类继承了Object类,所以Thread也能够调用者三个方法)?事实上这个问题非常easy。因为每一个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁。当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁。假设通过线程来操作。就非常复杂了。

上面已经提到,假设调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)

调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待兴许再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停运行一段时间。从而让其它线程有机会继续运行。但它并不释放对象锁);

notify()方法可以唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话。则仅仅能唤醒当中一个线程。详细唤醒哪个线程则不得而知。

相同地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor。因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

nofityAll()方法可以唤醒全部正在等待该对象的monitor的线程,这一点与notify()方法是不同的。

这里要注意一点:notify()和notifyAll()方法仅仅是唤醒等待该对象的monitor的线程。并不决定哪个线程可以获取到monitor。

举个简单的样例:假如有三个线程Thread1、Thread2和Thread3都在等待对象objectA的monitor,此时Thread4拥有对象objectA的monitor,当在Thread4中调用objectA.notify()方法之后,Thread1、Thread2和Thread3仅仅有一个能被唤醒。

注意。被唤醒不等于立马就获取了objectA的monitor。假若在Thread4中调用objectA.notifyAll()方法,则Thread1、Thread2和Thread3三个线程都会被唤醒,至于哪个线程接下来可以获取到objectA的monitor就详细依赖于操作系统的调度了。

上面尤其要注意一点。一个线程被唤醒不代表马上获取了对象的monitor。仅仅有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁运行。

package com.gpl.concurrent.lock;
public class Test {
    public static Object object = new Object();
    public static void main(String[] args) {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.setName("我是Thread1");
        thread2.setName("我是Thread2");
        thread1.start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread2.start();
    }

    static class Thread1 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                try {
                	String name=Thread.currentThread().getName();
                	System.out.println(name+"运行了");
                    object.wait();
                    System.out.println(name+"继续运行");
                } catch (InterruptedException e) {
                }
                System.out.println("线程"+Thread.currentThread().getName()+"获取到了锁");
            }
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
            	String name=Thread.currentThread().getName();
            	System.out.println(name+"运行了");
                object.notify();
                System.out.println("线程"+Thread.currentThread().getName()+"调用了object.notify()");
            }
            System.out.println("线程"+Thread.currentThread().getName()+"释放了锁");
        }
    }
}

结果:

在Java中,能够通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信。在线程中调用wait()方法。将堵塞等待其它线程的通知(其它线程调用notify()方法或notifyAll()方法),在线程中调用notify()方法或notifyAll()方法,将通知其它线程从wait()方法处返回。

Object是全部类的超类,它有5个方法组成了等待/通知机制的核心:notify()、notifyAll()、wait()、wait(long)和wait(long,int)。

在Java中,全部的类都从Object继承而来。因此,全部的类都拥有这些共同拥有方法可供使用。并且。因为他们都被声明为final。因此在子类中不能覆写不论什么一个方法。

这里具体说明一下各个方法在使用中须要注意的几点:

1、wait()

public final void wait()  throws InterruptedException,IllegalMonitorStateException

该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。在调用wait()之前。线程必需要获得该对象的对象级别锁,即仅仅能在同步方法或同步块中调用wait()方法。

进入wait()方法后。当前线程释放锁。在从wait()返回前,线程与其它线程竞争又一次获得锁。假设调用wait()时,没有持有适当的锁,则抛出IllegalMonitorStateException。它是RuntimeException的一个子类。因此,不需要try-catch结构。

2、notify()

public final native void notify() throws IllegalMonitorStateException

该方法也要在同步方法或同步块中调用,即在调用前。线程也必需要获得该对象的对象级别锁。的假设调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。

该方法用来通知那些可能等待该对象的对象锁的其它线程。假设有多个线程等待,则线程规划器随意挑选出当中一个wait()状态的线程来发出通知。并使它等待获取该对象的对象锁(notify后,当前线程不会立即释放该对象锁。wait所在的线程并不能立即获取该对象锁。要等到程序退出synchronized代码块后,当前线程才会释放锁,wait所在的线程也才干够获取该对象锁),但不惊动其它相同在等待被该对象notify的线程们。当第一个获得了该对象锁的wait线程执行完成以后,它会释放掉该对象锁,此时假设该对象没有再次使用notify语句,则即便该对象已经空暇。其它wait状态等待的线程因为没有得到该对象的通知,会继续堵塞在wait状态。直到这个对象发出一个notify或notifyAll。

这里须要注意:它们等待的是被notify或notifyAll,而不是锁。这与以下的notifyAll()方法执行后的情况不同。

3、notifyAll()

public final native void notifyAll() throws IllegalMonitorStateException

该方法与notify()方法的工作方式同样。重要的一点差异是:

notifyAll使全部原来在该对象上wait的线程统统退出wait的状态(即全部被唤醒。不再等待notify或notifyAll,但因为此时还没有获取到该对象锁,因此还不能继续往下运行)。变成等待获取该对象上的锁。一旦该对象锁被释放(notifyAll线程退出调用了notifyAll的synchronized代码块的时候)。他们就会去竞争。假设当中一个线程获得了该对象锁,它就会继续往下运行,在它退出synchronized代码块。释放锁后,其它的已经被唤醒的线程将会继续竞争获取该锁。一直进行下去。直到全部被唤醒的线程都运行完成。

4、wait(long)和wait(long,int)

显然。这两个方法是设置等待超时时间的,后者在超值时间上加上ns。精度也难以达到。因此,该方法非常少使用。对于前者。假设在等待线程接到通知或被中断之前,已经超过了指定的毫秒数,则它通过竞争又一次获得锁。并从wait(long)返回。另外。须要知道,假设设置了超时时间。当wait()返回时。我们不能确定它是由于接到了通知还是由于超时而返回的,由于wait()方法不会返回不论什么相关的信息。

但一般能够通过设置标志位来推断,在notify之前改变标志位的值。在wait()方法后读取该标志位的值来推断,当然为了保证notify不被遗漏,我们还须要另外一个标志位来循环推断是否调用wait()方法。

深入理解:

假设线程调用了对象的wait()方法。那么线程便会处于该对象的等待池中。等待池中的线程不会去竞争该对象的锁。

当有线程调用了对象的notifyAll()方法(唤醒全部wait线程)或notify()方法(仅仅随机唤醒一个wait线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。

优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用wait()方法。它才会又一次回到等待池中。而竞争到对象锁的线程则继续往下运行。直到运行完了synchronized代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

參考:http://www.cnblogs.com/dolphin0520/p/3920385.html

时间: 2024-10-31 09:27:49

Java并发之线程间协作Object的wait()、notify()、notifyAll()的相关文章

java并发编程 线程间协作

线程间协作 1. 等待和通知 等待和通知的标准形式 等待方: 获取对象锁 循环中判断条件是否满足,不调用wait()方法 条件满足执行业务逻辑 通知方: 获取对象所 改变条件 通知所有等待在对象的线程 2. wait.notify/notifyAll wait:导致当前线程等待,直到有其他线程调用当前同步监视器的notify()或notifyall()方法来唤醒此线程.wait()方法有三种形式--无时间参数的wait(一直等待,直到其他线程通知),带毫秒参数的waut()和带毫秒和纳秒参数的w

thread_线程间协作:wait、notify、notifyAll和Condition

经典模式:生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态.然后等待消费者消费了商品,然后消费者通知生产者队列有空间了.同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了.这种互相通信

java中关于线程间协作所用关键字synchronized,wait,notify的用法

wait/notify()关键字适用于一个线程通知另一个线程所需的条件状态已就绪,最常用于线程在循环中休眠直到获取特定条件的场景. 例如,一个线程一直等待直到队列中有一个组件能够处理:当组件添加到队列时,另一个线程能够唤醒这个等待的线程. 如下代码演示线程间的协作: package thread_waitnotify; public class ProductFactory { class Worker implements Runnable { private final Object loc

线程间协作:wait、notify、notifyAll

转载:http://blog.csdn.net/ns_code/article/details/17225469      在Java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信.在线程中调用wait()方法,将阻塞等待其他线程的通知(其他线程调用notify()方法或notifyAll()方法),在线程中调用notify()方法或notifyAll()方法,将通知其他线程从wait()方法处返回. Object是所有

Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态.然后等待消费者消费了商品,然后消费者通知生产者队列有空间了.同样地,当

JAVA线程间协作:wait.notify.notifyAll

JAVA的进程同步是通过synchronized()来实现的,须要说明的是,JAVA的synchronized()方法相似于操作系统概念中的相互排斥内存块.在JAVA中的Object类型中.都是带有一个内存锁的,在有线程获取该内存锁后.其它线程无法訪问该内存.从而实现JAVA中简单的同步.相互排斥操作. 明确这个原理.就能理解为什么synchronized(this)与synchronized(static XXX)的差别了.synchronized就是针对内存区块申请内存锁,thiskeywo

19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对

Java多线程之线程的状态以及线程间协作通信导致的状态变换

一:线程的状态以及变化图 Java中线程中状态可分为五种:New(新建状态),Runnable(就绪状态),Running(运行状态),Blocked(阻塞状态),Dead(死亡状态). New:新建状态,当线程创建完成时为新建状态,即new Thread(...),还没有调用start方法时,线程处于新建状态. Runnable:就绪状态,当调用线程的的start方法后,线程进入就绪状态,等待CPU资源.处于就绪状态的线程由Java运行时系统的线程调度程序(thread scheduler)来

Java 多线程 :入门(2)- 线程间协作:挂起当前线程(wait)与通知其他线程继续执行(notify\ notifyAll)

首先,之前我理解起来比较混沌的是到底谁是‘锁’这个问题,因为线程间协作的核心就是锁的交换,通过每个线程的“获得锁”与“释放锁”来实现. 锁,也叫“互斥”,是一种机制,通过控制一个对象在一定代码段(或方法内)同时只能被一个线程所访问,来实现所谓的(对于这个特定对象的)“线程安全”. 1.先看一个从网上扒来的最基本款示例,原文 http://www.cnphp6.com/archives/62258,写的很棒很清晰,我这里略微改了一两句: public class TestNotifyByFlag