Java中线程的等待与唤醒_动力节点Java学院整理

wait(), notify(), notifyAll()等方法介绍

在Object.java中,定义了wait(), notify()和notifyAll()等接口。wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。

Object类中关于等待/唤醒的API详细信息如下:

notify()        -- 唤醒在此对象监视器上等待的单个线程。
notifyAll()   -- 唤醒在此对象监视器上等待的所有线程。
wait()                                         -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout)                    -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout, int nanos)  -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。

 wait()和notify()示例

下面通过示例演示"wait()和notify()配合使用的情形"。

// WaitTest.java的源码
class ThreadA extends Thread{
  public ThreadA(String name) {
    super(name);
  }
  public void run() {
    synchronized (this) {
      System.out.println(Thread.currentThread().getName()+" call notify()");
      // 唤醒当前的wait线程
      notify();
    }
  }
}
public class WaitTest {
  public static void main(String[] args) {
    ThreadA t1 = new ThreadA("t1");
    synchronized(t1) {
      try {
        // 启动“线程t1”
        System.out.println(Thread.currentThread().getName()+" start t1");
        t1.start();
        // 主线程等待t1通过notify()唤醒。
        System.out.println(Thread.currentThread().getName()+" wait()");
        t1.wait();
        System.out.println(Thread.currentThread().getName()+" continue");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

运行结果:

main start t1
main wait()
t1 call notify()
main continue

结果说明:

如下图,说明了“主线程”和“线程t1”的流程。

(01) 注意,图中"主线程" 代表“主线程main”。"线程t1" 代表WaitTest中启动的“线程t1”。 而“锁” 代表“t1这个对象的同步锁”。

(02) “主线程”通过 new ThreadA("t1") 新建“线程t1”。随后通过synchronized(t1)获取“t1对象的同步锁”。然后调用t1.start()启动“线程t1”。

(03) “主线程”执行t1.wait() 释放“t1对象的锁”并且进入“等待(阻塞)状态”。等待t1对象上的线程通过notify() 或 notifyAll()将其唤醒。

(04) “线程t1”运行之后,通过synchronized(this)获取“当前对象的锁”;接着调用notify()唤醒“当前对象上的等待线程”,也就是唤醒“主线程”。

(05) “线程t1”运行完毕之后,释放“当前对象的锁”。紧接着,“主线程”获取“t1对象的锁”,然后接着运行。

对于上面的代码?曾经有个朋友问到过:t1.wait()应该是让“线程t1”等待;但是,为什么却是让“主线程main”等待了呢?

在解答该问题前,我们先看看jdk文档中关于wait的一段介绍:

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
In other words, this method behaves exactly as if it simply performs the call wait(0).
The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

注意:jdk的解释中,说wait()的作用是让“当前线程”等待,而“当前线程”是指正在cpu上运行的线程!

这也意味着,虽然t1.wait()是通过“线程t1”调用的wait()方法,但是调用t1.wait()的地方是在“主线程main”中。而主线程必须是“当前线程”,也就是运行状态,才可以执行t1.wait()。所以,此时的“当前线程”是“主线程main”!因此,t1.wait()是让“主线程”等待,而不是“线程t1”!

wait(long timeout)和notify()

wait(long timeout)会让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。

下面的示例就是演示wait(long timeout)在超时情况下,线程被唤醒的情况。

// WaitTimeoutTest.java的源码
class ThreadA extends Thread{
  public ThreadA(String name) {
    super(name);
  }
  public void run() {
    System.out.println(Thread.currentThread().getName() + " run ");
    // 死循环,不断运行。
    while(true)
      ;
  }
}
public class WaitTimeoutTest {
  public static void main(String[] args) {
    ThreadA t1 = new ThreadA("t1");
    synchronized(t1) {
      try {
        // 启动“线程t1”
        System.out.println(Thread.currentThread().getName() + " start t1");
        t1.start();
        // 主线程等待t1通过notify()唤醒 或 notifyAll()唤醒,或超过3000ms延时;然后才被唤醒。
        System.out.println(Thread.currentThread().getName() + " call wait ");
        t1.wait(3000);
        System.out.println(Thread.currentThread().getName() + " continue");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

运行结果:

main start t1
main call wait
t1 run         // 大约3秒之后...输出“main continue”
main continue

结果说明:

如下图,说明了“主线程”和“线程t1”的流程。

(01) 注意,图中"主线程" 代表WaitTimeoutTest主线程(即,线程main)。"线程t1" 代表WaitTest中启动的线程t1。 而“锁” 代表“t1这个对象的同步锁”。

(02) 主线程main执行t1.start()启动“线程t1”。

(03) 主线程main执行t1.wait(3000),此时,主线程进入“阻塞状态”。需要“用于t1对象锁的线程通过notify() 或者 notifyAll()将其唤醒” 或者 “超时3000ms之后”,主线程main才进入到“就绪状态”,然后才可以运行。

(04) “线程t1”运行之后,进入了死循环,一直不断的运行。

(05) 超时3000ms之后,主线程main会进入到“就绪状态”,然后接着进入“运行状态”。

wait() 和 notifyAll()

通过前面的示例,我们知道 notify() 可以唤醒在此对象监视器上等待的单个线程。

下面,我们通过示例演示notifyAll()的用法;它的作用是唤醒在此对象监视器上等待的所有线程。

public class NotifyAllTest {
   private static Object obj = new Object();
   public static void main(String[] args) {
     ThreadA t1 = new ThreadA("t1");
     ThreadA t2 = new ThreadA("t2");
     ThreadA t3 = new ThreadA("t3");
     t1.start();
     t2.start();
     t3.start();
     try {
       System.out.println(Thread.currentThread().getName()+" sleep(3000)");
       Thread.sleep(3000);
     } catch (InterruptedException e) {
       e.printStackTrace();
     }
     synchronized(obj) {
       // 主线程等待唤醒。
       System.out.println(Thread.currentThread().getName()+" notifyAll()");
       obj.notifyAll();
     }
   }
   static class ThreadA extends Thread{
     public ThreadA(String name){
       super(name);
     }
     public void run() {
       synchronized (obj) {
         try {
           // 打印输出结果
           System.out.println(Thread.currentThread().getName() + " wait");
           // 唤醒当前的wait线程
           obj.wait();
           // 打印输出结果
           System.out.println(Thread.currentThread().getName() + " continue");
         } catch (InterruptedException e) {
           e.printStackTrace();
         }
       }
     }
   }
 }

运行结果:

t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue 

结果说明:

参考下面的流程图。

(01) 主线程中新建并且启动了3个线程"t1", "t2"和"t3"。

(02) 主线程通过sleep(3000)休眠3秒。在主线程休眠3秒的过程中,我们假设"t1", "t2"和"t3"这3个线程都运行了。以"t1"为例,当它运行的时候,它会执行obj.wait()等待其它线程通过notify()或额nofityAll()来唤醒它;相同的道理,"t2"和"t3"也会等待其它线程通过nofity()或nofityAll()来唤醒它们。

(03) 主线程休眠3秒之后,接着运行。执行 obj.notifyAll() 唤醒obj上的等待线程,即唤醒"t1", "t2"和"t3"这3个线程。 紧接着,主线程的synchronized(obj)运行完毕之后,主线程释放“obj锁”。这样,"t1", "t2"和"t3"就可以获取“obj锁”而继续运行了!

为什么notify(), wait()等函数定义在Object中,而不是Thread中

Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。

wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行!

OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。现在,请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?答案是:依据“对象的同步锁”。

负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才能获取到“对象的同步锁”进而继续运行。

总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。

以上所述是小编给大家介绍的Java中线程的等待与唤醒,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll)

    本篇我们来研究一下 wait() notify() notifyAll() . DEMO1: wait() 与 notify() public class Test { static class ThreadOne extends Thread { private Callback mCallback; @Override public void run() { work(); if (mCallback != null) { mCallback.onResult(false); } } //

  • Java多线程--让主线程等待所有子线程执行完毕在执行

    朋友让我帮忙写个程序从文本文档中导入数据到oracle数据库中,技术上没有什么难度,文档的格式都是固定的只要对应数据库中的字段解析就行了,关键在于性能. 数据量很大百万条记录,因此考虑到要用多线程并发执行,在写的过程中又遇到问题,我想统计所有子进程执行完毕总共的耗时,在第一个子进程创建前记录当前时间用System.currentTimeMillis()在最后一个子进程结束后记录当前时间,两次一减得到的时间差即为总共的用时,代码如下 long tStart = System.currentTime

  • Java线程重复执行以及操作共享变量的代码示例

    1.题目:主线程执行10次,子线程执行10次,此过程重复50次 代码: package com.Thread.test; /* * function:主线程执行10次,子线程执行10次, * 此过程重复50次 */ public class ThreadProblem { public ThreadProblem() { final Business bus = new Business(); new Thread(new Runnable() { public void run() { for

  • Java实现等待所有子线程结束后再执行一段代码的方法

    本文实例讲述了Java实现等待所有子线程结束后再执行一段代码的方法.分享给大家供大家参考,具体如下: 今天有一个需求是:在一个方法中开启了一个子线程来执行操作,返回值依赖于子线程的执行结果,这样如果要返回正确的值,就需要开启子线程后 主线程等待子线程,然后子线程执行结束后,主线程再继续执行. 主线程等待子线程需要用到:CountDownLatch 代码如下: import java.util.concurrent.CountDownLatch; public class Counter { pu

  • 详解Java利用ExecutorService实现同步执行大量线程

    自从java1.5以后,官网就推出了Executor这样一个类,这个类,可以维护我们的大量线程在操作临界资源时的稳定性. 先上一段代码吧: TestRunnable.java public class TestRunnable implements Runnable { private String name; public TestRunnable(String name) { this.name = name; } @Override public void run() { while (t

  • Java并发编程示例(一):线程的创建和执行

    开门见山 在IT圈里,每当我们谈论并发时,必定会说起在一台计算机上同时运行的一系列线程.如果这台电脑上有多个处理器或者是一个多核处理器,那么这时是实实在在的"同时运行":但是,如果计算机只有一个单核处理器,那么这时的"同时运行"只是表象而已. 所有的现代操作系统全部支持任务的并发执行.你可以边听音乐,边上网看新闻,还不耽误首发电子邮件.我们可以说,这种并发是 进程级并发 .在进程内部,我也可以看到有许许多多的并发任务.我们把运行在一个进程里面的并发任务称 线程. 和

  • Java并发编程示例(六):等待线程执行终止

    在某些场景下,我们必须等待线程执行完成才能进行下一步工作.例如,某些程序在开始执行之前,需要先初始化一些资源.这时,我们可以启动一个线程专门来做初始化任务,等到线程任务完成后,再去执行其他部分. 为此,Thread类为我们提供了join()方法.当我们使用线程对象调用此方法时,正在掉调用的线程对象将被推迟到被调用对象执行完成后再开始执行. 在本节,示例程序演示等待初始化方法完成后,再去执行其他任务. 知其然 按照下面所示步骤,完成示例程序. 1.创建一个名为DataSourcesLoader的类

  • java基本教程之java线程等待与java唤醒线程 java多线程教程

    本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. wait()和notify()3. wait(long timeout)和notify()4. wait() 和 notifyAll()5. 为什么notify(), wait()等函数定义在Object中,而不是Thread中 wait(), notify(), notifyAll()等方法介绍在Object.java中,定义了wait(), notify()

  • java多线程处理执行solr创建索引示例

    复制代码 代码如下: public class SolrIndexer implements Indexer, Searcher, DisposableBean { //~ Static fields/initializers ============================================= static final Logger logger = LoggerFactory.getLogger(SolrIndexer.class); private static fi

  • Java 多线程有序执行的几种方法总结

    Java 多线程有序执行的几种方法总结 同事无意间提出了这个问题,亲自实践了两种方法.当然肯定还会有更多更好的方法. 方法一 import java.util.concurrent.atomic.AtomicInteger; public class OrderedThread1 { static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws Interrupte

随机推荐