一、sleep()

sleep()方法源码:

   /** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does not lose ownership of any monitors. * * @param  millis *         the length of time to sleep in milliseconds * * @throws  IllegalArgumentException *          if the value of {@code millis} is negative * * @throws  InterruptedException *          if any thread has interrupted the current thread. The *          <i>interrupted status</i> of the current thread is *          cleared when this exception is thrown. */  public static native void sleep(long millis) throws InterruptedException; 

sleep()方法来自于Thread类,从源码给出的解释来看,sleep()方法可以做到如下几点:

(1)sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;

(2)sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法时,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。

(3)在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。

代码演示:

public class Main {  public static void main(String[] args) {  Main main = new Main();  main.startThread();  }  /** * 启动线程 */  public void startThread() {  Thread t = new Thread(new Runnable() {  @Override  public void run() {  System.out.println("开始执行线程。。。");  System.out.println("进入睡眠状态。。。");  try {  Thread.sleep(3000);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("线程结束。。。");  }  });  t.start();  }
}  

运行结果:

开始执行线程。。。
进入睡眠状态。。。
线程结束。。。

从运行的结果来看,我们可以看出程序虽然在运行过程中中断了3秒,但是在3秒结束之后依然会继续执行代码,直到运行结束。在睡眠的期间内,线程会一直持有monitor对象。

二、wait()

wait()方法源码:

    /** * Causes the current thread to wait until another thread invokes the * {@link java.lang.Object#notify()} method or the * {@link java.lang.Object#notifyAll()} method for this object. * In other words, this method behaves exactly as if it simply * performs the call {@code wait(0)}. * <p> * 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 {@code notify} method or the * {@code notifyAll} method. The thread then waits until it can * re-obtain ownership of the monitor and resumes execution. * <p> * As in the one argument version, interrupts and spurious wakeups are * possible, and this method should always be used in a loop: * <pre> *     synchronized (obj) { *         while (<condition does not hold>) *             obj.wait(); *         ... // Perform action appropriate to condition *     } * </pre> * This method should only be called by a thread that is the owner * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * * @exception  IllegalMonitorStateException  if the current thread is not *               the owner of the object's monitor. * @exception  InterruptedException if any thread interrupted the *             current thread before or while the current thread *             was waiting for a notification.  The <i>interrupted *             status</i> of the current thread is cleared when *             this exception is thrown. * @see        java.lang.Object#notify() * @see        java.lang.Object#notifyAll() */  public final void wait() throws InterruptedException {  wait(0);  } 

首先wait()是属于Object类的方法,从源码给出的解释来看,wait()方法可以做到如下几点:

(1)wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);其他线程可以访问;

(2)每个线程必须持有该对象的monitor。如果在当前线程中调用wait()方法之后,该线程就会释放monitor的持有对象并让自己处于等待状态。

(3)如果想唤醒一个正在等待的线程,那么需要开启一个线程通过notify()或者notifyAll()方法去通知正在等待的线程获取monitor对象。如此,该线程即可打破等待的状态继续执行代码。

(4)wiat()必须放在synchronized block中,否则会在program runtime时扔出”java.lang.IllegalMonitorStateException“异常。

代码演示:

public class Main {  public static void main(String[] args) {  Main main = new Main();  main.startThread();  }  /** * 线程锁 */  private final Object object = new Object();  /** * 启动线程 */  public void startThread() {  Thread t = new Thread(new Runnable() {  @Override  public void run() {  System.out.println("开始执行线程。。。");  System.out.println("进入等待状态。。。");  synchronized (object) {  try {  object.wait();  } catch (InterruptedException e) {  e.printStackTrace();  }  }  System.out.println("线程结束。。。");  }  });  t.start();  }
}  

从代码来看,在执行线程和线程结束之间,我们先让该线程获取object对象作为自己的object's monitor,然后调用了object对象的wait()方法从而让其进入等待状态。那么程序运行的结果如下:

开始执行线程。。。
进入等待状态。。。

程序在未被唤醒之后,将不再打印“线程结束”,并且程序无法执行完毕一直处于等待状态。

那么从以上的理论和实践来分析,我们能得出如下结论:

(1)在线程的运行过程中,调用该线程持有monitor对象的wait()方法时,该线程首先会进入等待状态,并将自己持有的monitor对象释放。

(2)如果一个线程正处于等待状态时,那么唤醒它的办法就是开启一个新的线程,通过notify()或者notifyAll()的方式去唤醒。当然,需要注意的一点就是,必须是同一个monitor对象。

(3)sleep()方法虽然会使线程中断,但是不会将自己的monitor对象释放,在中断结束后,依然能够保持代码继续执行。

三、notify()和notifyAll()

说完了sleep()和wait()方法之后,我们接下来讨论一下Object类中的另外两个与wait()相关的方法。首先还是通过源码的方式让大家先初步了解一下:

    /** * Wakes up a single thread that is waiting on this object's * monitor. If any threads are waiting on this object, one of them * is chosen to be awakened. The choice is arbitrary and occurs at * the discretion of the implementation. A thread waits on an object's * monitor by calling one of the {@code wait} methods. * <p> * The awakened thread will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened thread will * compete in the usual manner with any other threads that might be * actively competing to synchronize on this object; for example, the * awakened thread enjoys no reliable privilege or disadvantage in being * the next thread to lock this object. * <p> * This method should only be called by a thread that is the owner * of this object's monitor. A thread becomes the owner of the * object's monitor in one of three ways: * <ul> * <li>By executing a synchronized instance method of that object. * <li>By executing the body of a {@code synchronized} statement *     that synchronizes on the object. * <li>For objects of type {@code Class,} by executing a *     synchronized static method of that class. * </ul> * <p> * Only one thread at a time can own an object's monitor. * * @exception  IllegalMonitorStateException  if the current thread is not *               the owner of this object's monitor. * @see        java.lang.Object#notifyAll() * @see        java.lang.Object#wait() */  public final native void notify(); 
先来看下notify()这个方法,通过阅读源码我们可以总结一下几点:
(1)当一个线程处于wait()状态时,也即等待它之前所持有的object's monitor被释放,通过notify()方法可以让该线程重新处于活动状态,从而去抢夺object's monitor,唤醒该线程。
       (2)如果多个线程同时处于等待状态,那么调用notify()方法只能随机唤醒一个线程。
       (3)在同一时间内,只有一个线程能够获得object's monitor,执行完毕之后,则再将其释放供其它线程抢占。
当然,如何使线程成为object‘s monitor的持有者,我会在多线程的其他博客中讲解。
接下来,我们再来看看notifyAll()方法:
    /** * Wakes up all threads that are waiting on this object's monitor. A * thread waits on an object's monitor by calling one of the * {@code wait} methods. * <p> * The awakened threads will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened threads * will compete in the usual manner with any other threads that might * be actively competing to synchronize on this object; for example, * the awakened threads enjoy no reliable privilege or disadvantage in * being the next thread to lock this object. * <p> * This method should only be called by a thread that is the owner * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * * @exception  IllegalMonitorStateException  if the current thread is not *               the owner of this object's monitor. * @see        java.lang.Object#notify() * @see        java.lang.Object#wait() */  public final native void notifyAll();  
那么顾名思义,notifyAll()就是用来唤醒正在等待状态中的所有线程的,不过也需要注意以下几点:
(1)notifyAll()只会唤醒那些等待抢占指定object's monitor的线程,其他线程则不会被唤醒。
       (2)notifyAll()只会一个一个的唤醒,而并非统一唤醒。因为在同一时间内,只有一个线程能够持有object's monitor
       (3)notifyAll()只是随机的唤醒线程,并非有序唤醒。
那么如何做到有序唤醒是我们接下来要讨论的问题。

notify()实现有序唤醒的思路和实现

就上节提出的问题,我们在这节中可以进行一下思考和讨论。
首先,简单来说,我们需要去解决的其实就是对于多线程针对object's monitor的有序化。那么根据这一思路,我直接上代码:
public class MyThreadFactory {  // 线程A是否处于等待状态的标志  private boolean isThreadAWaiting;  // 线程B是否处于等待状态的标志  private boolean isThreadBWaiting;  // 线程C是否处于等待状态的标志  private boolean isThreadCWaiting;  public MyThreadFactory() {  isThreadAWaiting = true;  isThreadBWaiting = true;  isThreadCWaiting = true;  }  /** * 对象锁 */  private final Object object = new Object();  /** * 该线程作为一个唤醒线程 */  public void startWakenThread() {  Thread t = new Thread(new Runnable() {  @Override  public void run() {  synchronized (object) {  System.out.println("唤醒线程开始执行...");  // 首先释放线程A  quitThreadA();  }  }  });  t.start();  }  /** * 启动线程A */  public void startThreadA() {  Thread t = new Thread(new Runnable() {  @Override  public void run() {  synchronized (object) {  System.out.println("线程A开始等待...");  try {  for (; ; ) {  if (!isThreadAWaiting) break;  object.wait();  }  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("线程A结束...");  // 线程A结束后,暂停2秒释放线程B  try {  Thread.sleep(2000);  } catch (InterruptedException e) {  e.printStackTrace();  }  quitThreadB();  }  }  });  t.start();  }  /** * 启动线程B */  public void startThreadB() {  Thread t = new Thread(new Runnable() {  @Override  public void run() {  synchronized (object) {  System.out.println("线程B开始等待...");  try {  for (; ; ) {  if (!isThreadBWaiting) break;  object.wait();  }  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("线程B结束...");  // 线程B结束后,暂停2秒释放线程C  try {  Thread.sleep(2000);  } catch (InterruptedException e) {  e.printStackTrace();  }  quitThreadC();  }  }  });  t.start();  }  /** * 启动线程C */  public void startThreadC() {  Thread t = new Thread(new Runnable() {  @Override  public void run() {  synchronized (object) {  System.out.println("线程C开始等待...");  try {  for (; ; ) {  if (!isThreadCWaiting) break;  object.wait();  }  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("线程C结束...");  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("所有线程执行完毕!");  }  }  });  t.start();  }  /** * 线程A退出等待 */  private void quitThreadA() {  isThreadAWaiting = false;  object.notify();  }  /** * 线程B退出等待 */  private void quitThreadB() {  isThreadBWaiting = false;  object.notify();  }  /** * 线程C退出等待 */  private void quitThreadC() {  isThreadCWaiting = false;  object.notify();  }
} 
在以上代码中,我写了三个线程A,B,C用来作为等待线程,并且最后通过一个唤醒线程来唤醒这三个线程。
我的思路是这样的:
(1)通过notify()方法可以保证每次只唤醒一个线程,但是不能确保唤醒的是哪个线程。
(2)在线程A,B,C中,添加for或者while循环的方式使其进入无限等待的状态。这样能够保证notify()无论如何都不能唤醒线程。
(3)分别给A,B,C线程设置各自的标记,如果要唤醒该线程的话,就改变其状态并且跳出死循环,在最后执行下一个线程。
那么最终调用的main函数如下:
    public static void main(String[] args) {  MyThreadFactory factory = new MyThreadFactory();  factory.startThreadA();  factory.startThreadB();  factory.startThreadC();  try {  Thread.sleep(3000);  } catch (InterruptedException e) {  e.printStackTrace();  }  factory.startWakenThread();  }

运行结果:

线程A开始等待...
线程B开始等待...
线程C开始等待...
唤醒线程开始执行...
线程A结束...
线程B结束...
线程C结束...
所有线程执行完毕...

Java基础——Java多线程中sleep()、wait()和notify()相关推荐

  1. java wait notifyall_Java多线程中的wait与notify,notifyall例子

    文章来给各位介绍一下Java多线程中的wait与notify,notifyall例子,希望文章能给各位朋友带来帮助哦. 在Java多线程编程中,wait()的作用的是让当前线程进入阻塞状态,notif ...

  2. Java基础、多线程、JVM、集合八股文自述(持续更新)

    Java基础.多线程.JVM.集合八股文自述 一.Java基础 1.1 object类有哪些方法? getClass().hashCode().equals().clone().toString(). ...

  3. Java基础-Java中常用的锁机制与使用

    Java基础-Java中常用的锁机制与使用 锁lock或互斥mutex是一种同步机制,主要用于在存在多线程的环境中强制对资源进行访问限制.锁的主要作用为强制实施互斥排他以及并发控制策略.锁一般需要硬件 ...

  4. Java基础-Java中的堆内存和离堆内存机制

    Java基础-Java中的堆内存和离堆内存机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 转载于:https://www.cnblogs.com/yinzhengjie/p/9 ...

  5. Java基础-JAVA中常见的数据结构介绍

    Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...

  6. Java基础-Java中的内存分配与回收机制

    Java基础-Java中的内存分配与回收机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. 二. 转载于:https://www.cnblogs.com/yinzhengji ...

  7. Java基础之Java8中Map的compute的使用

    Java基础之Java8中Map的compute的使用 一.介绍 Java8更新后,Map接口中提供了compute方法.下面我们先看看官方文档的对它的使用说明. 如果看完上面的还是不太明白的话,看下 ...

  8. [重学Java基础][Java IO流][Exter.2]IO流中几种不同的读写方法的区别

    [重学Java基础][Java IO流][Exter.2]IO流中几种不同的读写方法的区别 Read 读入方法 read(): 一般是这种形式 public int read() 1.从流数据中读取的 ...

  9. Java基础之Java8中map和flatMap的使用

    Java基础之Java8中map和flatMap的使用 一.介绍 首先,看下map和flatMap的官方文档说明 map flatMap 其实单纯的看api说明还是比较抽象,下面我将以几个实战例子来帮 ...

  10. Java基础----Java编程语言概述

    Java基础----Java编程语言概述 文章目录 Java基础----Java编程语言概述 Java编程语言概述 1-1 软件开发介绍 1.1软件开发介绍 1.2 常用命令 1-2 计算机编程语言介 ...

最新文章

  1. 用于自动驾驶的实时 YUV 多任务 CNN
  2. .NET架构与模式探索
  3. Cissp-【第2章 资产安全】-2021-1-14(163页-185页)
  4. 20165218 2017-2018-1 《Java程序设计》第四周学习总结
  5. Mac下PyCharm CE 配置PyQt5环境
  6. eclipse下的tomcat内存设置大小
  7. 日常生活收缩毛孔几个小妙招 - 健康程序员,至尚生活!
  8. 虚拟服务器 端口管理,Apache服务配置虚拟主机(基于域名、端口、IP地址)与简单访问权限管理...
  9. linux yum选择版本mysql_linux yum安装指定版本mysql
  10. 执行nvidia-smi出错
  11. arduino怎么和C语言程序通信,c – 如何将arduino库与标准C代码一起使用
  12. LINUX C# 加载本地库的范例代码
  13. 快手用户群体分析_抖音、快手竞品分析报告
  14. mysql2000数据库四合一_sql2000四合一版下载|microsoft sql server2000 简体中文4合一版附sql 2000 sp4 补丁_ - 极光下载站...
  15. [python-opencv]滑动验证码打码
  16. 国产免费倾斜摄影模型在线发布平台,一键查看、编辑、分享场景!
  17. BZOJ1022 [SHOI2008]小约翰的游戏John
  18. es的DSL语句查询
  19. 基于vspd DLL二次开发的虚拟串口工具
  20. 达梦物化视图概念及简单示例

热门文章

  1. mysql replace first_Java replaceFirst()方法
  2. 敏捷开发流程的8个步骤_敏捷开发——个体和互动高于流程和工具
  3. LSGO软件技术团队与信息1402班开展真人CS活动
  4. C#中 ??、 ?、 ?: 、?.、?[ ]、:
  5. ajax中怎么验证data,我应该在jQuery的ajax成功处理程序中验证响应数据吗?
  6. python @修饰符_python函数修饰符@的使用方法解析
  7. 解决:VS中进行Qt开发,编译时报错:打不开QWidgets.h等文件的问题
  8. REVERSE-PRACTICE-JarvisOJ-4
  9. 【POJ - 1741】Tree(树分治,容斥,点分治,模板题)
  10. 【牛客 - 330C】Applese 走迷宫(bfs)