• 调用sleep方法将时线程进入休眠状态

public class ThreadTest implements Runnable{

    @Override
public void run() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

  这段代码的意思就是让当前线程休眠1000毫秒,也就是1秒

对sleep方法的调用将抛出 InterruptedException, 因为异常不能跨线程传播回main(),所以必须在本地处理所有在任务内部产生的异常。

我们看下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.
*/
// BEGIN Android-changed: Implement sleep() methods using a shared native implementation.
public static void sleep(long millis) throws InterruptedException {
sleep(millis, 0);
}

  注释里有这么一句话:

The thread does not lose ownership of any monitors.

翻译过来就是说,调用sleep方法的线程不会失去对监视器的控制, 什么是监视器呢,我们在之前的文章中说过,就是对象锁。

所以, sleep方法使线程进入休眠状态,停止执行指定的时间, 但不会释放锁。

可以看到源码中直接调用了 sleep(long millis, int nanos)  这个方法的意思就是时线程休眠指定的毫秒数+指定的纳秒数

  • 调用wait方法线程进入等待状态,(在当前对象锁上等待),等待和休眠都属于阻塞,但是wait方法将释放锁

我们使用wait方法通常是因为线程需要等待某个外部条件发生变化,而改变这个条件超出了当前方法的控制能力。所以需要释放锁,让其他方法可以被调用,以产生某些条件变化

wait也有两种调用形式(有参和无参)

private synchronized void test() throws InterruptedException {
Thread thread = new Thread();
//使当前线程无限等待
thread.wait();
//使当前线程停止执行1000毫秒
thread.wait(1000);
//使当前线程停止执行1000毫秒 + 500纳秒
thread.wait(1000, 500);
}

对于指定时间的wait方法, 时间到期后线程会恢复执行,这里和sleep相似, 不同的是wait会释放锁

对于不指定时间的wait, 线程将无限等待,直到接收到notify()或者notifyAll()

点开源码

/**
* 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.
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of the object's monitor.
* @throws 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(), notify(), notifyAll()这些方法作为线程的功能, 却定义在基类Object中, 而不是在Thread

因为这些方法操作的锁也是所有对象的一部分

因此, wait(), notify(), notifyAll()都只能在同步方法或者同步控制块中调用, 也就是说调用该方法的线程必须已经获取了锁。 否则将得到IllegalMonitorStateException

而sleep因为不用操作锁, 因此可以在非同步方法中调用

通常我们在while循环里调用wait()方法, 本质就是需要检查某个特点的条件, 当收到notify/notifyAll时进行判断, 如果不满足, 则返回到wait中继续等待

notify()方法将唤醒等待同一个锁的线程中的某一个线程

/**
* 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.
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of this object's monitor.
* @see java.lang.Object#notifyAll()
* @see java.lang.Object#wait()
*/
@FastNative
public final native void notify();

  

这个方法的源码我看不到, 但可以看到注释,意思是说:

1, 如果当前对象的监视器上有等待的线程,则唤醒其中的一个

2, 唤醒哪一个呢?选择是随机的,取决于你的实现

因此,如果你想要使用notify, 就必须确保被唤醒的是恰当的任务。 所有任务(如果有)必须等待相同的条件,否则, 你不会知道是否唤醒了恰当的任务。当条件发生变化时, 只能有一个任务能够从中受益。

3, 唤醒的是调用了wait方法在此对象监视器上等待的线程

4, 被唤醒的线程并不会立即执行,要等到当前线程(调notify的线程)释放锁之后

5, 释放锁之后,被唤醒的线程也不一定会立即执行,如果当前还有其他线程在等待锁, 则该线程要和其他线程竞争

6,调用此方法(notify)的线程必须已经获得了对象监视器,即对象锁

7,任意时刻下,只有一个线程可以获得对象锁

8,获得对象锁的三种方法: 同步方法,同步块,同步静态方法(前文中说过,synhronized修饰staitc方法时, 锁住的是整个类)

notifyAll和notify的区别是: notifyAll()将唤醒在等待这个锁的所有任务

小结: sleep和wait的区别

两者都将使当前线程进入阻塞状态, sleep使当前线程休眠指定时间,wait使当前线程等待指定时间或无限等待。 

sleep不释放锁,休眠指定时间后会继续执行, wait 会释放锁, 等待指定时间后需要重新获取锁,不一定会立即执行

wait无限等待时, 必须调用notify/notifyAll才能被唤醒

wait因为要操作锁, 所以必须在同步方法/同步代码块中调用,否则会抛出运行时异常IllegalMonitorStateException, sleep不操作锁,没有限制

这一点上,notify/notifyAll和wait一样,调用这些方法的线程必须自己已经获得了对象锁

线程相关的一些其他方法:

setPriority()/getPriority()  设置/获取线程的优先级, 通常在run方法的开头去设定

当多个线程竞争一把锁时,调度器倾向于让优先权更高的线程先执行

在绝大多数时间里,所有线程应该以默认优先级(NORM_PRIORITY)运行

yield()方法--线程让步

告诉调度器,可以给别的线程使用cpu了,建议有相同优先级的其他线程先运行

但这只是给调度器的建议, 并不一定会被采纳

setDaemon()--设置当前线程为后台线程。 必须在线程启动之前调用才有效

后台线程并不属于程序中不可或缺的一部分, 因此, 当所有非后台线程结束时, 程序也就终止了, 同时杀死进程中的所有后台线程

cancel()终结任务。isCancled()检查线程的终结状态

thread.interrupt()中断线程 

thread.interrupted()检查线程中断状态

调用Executor.shutDownNow()时,executor将发送interrupt()调用给它启动的所有线程

interrupt方法可以中断任何要求抛出InterruptedException的调用(如sleep()),但不能中断正在试图获取锁或者执行io操作的线程。

所以对于io操作的线程, 我们可以关闭底层资源来释放锁。

乐观锁的概念:

乐观加锁, 就是说当我们执行某项计算时, 实际上没有使用互斥,但是当计算完成,准备更新资源时, 使用compareAndSet()方法,将旧值和新值一起传过去。

如果旧值和该对象之前保存的值不一致,就意味着有其他线程在计算期间修改了这个对象, 那么这个操作就失败

所以乐观锁就是说,我们是乐观的,保持数据为未锁定状态,并希望我们修改期间没有其他线程来操作它。  这通常是为了性能的提升

线程的4个状态:

新建: 线程被创建时, 短暂地处于这种状态

就绪:这种状态下,只要cpu把时间片给线程, 线程就可以运行

阻塞: 线程能够运行,但是有某个条件阻止它运行, 线程处于阻塞状态时, 调度器将忽略线程,直到它重新进入就绪状态

线程进入阻塞状态的原因可能有: 任务调用了sleep()或者wait(),  任务在等待某个输入/输出完成, 任务试图调用同步控制方法, 但对象锁不可用, 因为已经被另一个任务获取。

死亡:任务执行完run方法不可再运行

java多线程--wait和sleep的更多相关文章

  1. 40个Java多线程问题总结

    前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...

  2. Java多线程基础知识篇

    这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...

  3. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...

  4. Java多线程系列--“JUC锁”04之 公平锁(二)

    概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...

  5. Java多线程--让主线程等待子线程执行完毕

    使用Java多线程编程时经常遇到主线程需要等待子线程执行完成以后才能继续执行,那么接下来介绍一种简单的方式使主线程等待. java.util.concurrent.CountDownLatch 使用c ...

  6. Java多线程 2 线程的生命周期和状态控制

    一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...

  7. java 多线程 1 线程 进程

    Java多线程(一).多线程的基本概念和使用 2012-09-10 16:06 5108人阅读 评论(0) 收藏 举报  分类: javaSE综合知识点(14)  版权声明:本文为博主原创文章,未经博 ...

  8. 一起阅读《Java多线程编程核心技术》

    目录 第一章 Java多线程技能 (待续...)

  9. 第一章 Java多线程技能

    1.初步了解"进程"."线程"."多线程" 说到多线程,大多都会联系到"进程"和"线程".那么这两者 ...

  10. java从基础知识(十)java多线程(下)

    首先介绍可见性.原子性.有序性.重排序这几个概念 原子性:即一个操作或多个操作要么全部执行并且执行的过程不会被任何因素打断,要么都不执行. 可见性:一个线程对共享变量值的修改,能够及时地被其它线程看到 ...

随机推荐

  1. Saruman's Army

    直线上有N个点. 点i的位置是Xi.从这N个点中选择若干个,给它们加上标记. 对每一个点,其距离为R以内的区域里必须有带有标记的点(自己本身带有标记的点, 可以认为与其距离为 0 的地方有一个带有标记 ...

  2. 线程间协作的两种方式:wait、notify、notifyAll和Condition

    转载自海子: 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者 ...

  3. canvas——动画实例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 剑指offer 计划1(栈与队列)---java

    1.1.题目1 剑指 Offer 09. 用两个栈实现队列 1.2.解法 解法如题目所说.定义两个栈.这里假设第一个栈为a,第二个栈为b. 实现两个函数增加尾和删除头. 增加即直接push入第一个栈. ...

  5. IO流学习笔记(一)之FileWriter与FileReader

    IO流用来处理设备之间的数据传输 Java对数据的操作是通过流的方式 Java用于操作流的对象都在IO包中 流按照操作数据分为两种:字节流和字符流 流按流向分为:输入流和输出流 输入流和输出流是相对于 ...

  6. Python安装环境配置和多版本共存

    Python安装环境配置和多版本共存 1.环境变量配置: (1) 右键点击"计算机",然后点击"属性" (2) 然后点击"高级系统设置" ( ...

  7. Spring Boot 入门系列(二十四)多环境配置,3分钟搞定!

    之前讲过Spring Boot 的系统配置和自定义配置,实现了按照实际项目的要求配置系统的相关熟悉.但是,在实际项目开发过程中,需要面对不同的环境,例如:开发环境,测试环境,生产环境.各个环境的数据库 ...

  8. 安装或更新时,pip出错,“No module named ‘pip’”

    解决办法: 在pycharm终端(Terminal)中 首先执行 :python -m ensurepip 然后执行 :python -m pip install --upgrade pip

  9. Appium自动化(3) - adb无线连接手机的方法

    如果你还想从头学起Appium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1693896.html 前言 除了USB方式连接Andro ...

  10. Jenkins(6)- 新建用户

    如果想从头学起Jenkins的话,可以看看这一系列的文章哦 https://www.cnblogs.com/poloyy/category/1645399.html 进入用户管理 点击新建用户 填写新 ...