java多线程--wait和sleep
- 调用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的更多相关文章
- 40个Java多线程问题总结
前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...
- Java多线程基础知识篇
这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”04之 公平锁(二)
概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...
- Java多线程--让主线程等待子线程执行完毕
使用Java多线程编程时经常遇到主线程需要等待子线程执行完成以后才能继续执行,那么接下来介绍一种简单的方式使主线程等待. java.util.concurrent.CountDownLatch 使用c ...
- Java多线程 2 线程的生命周期和状态控制
一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...
- java 多线程 1 线程 进程
Java多线程(一).多线程的基本概念和使用 2012-09-10 16:06 5108人阅读 评论(0) 收藏 举报 分类: javaSE综合知识点(14) 版权声明:本文为博主原创文章,未经博 ...
- 一起阅读《Java多线程编程核心技术》
目录 第一章 Java多线程技能 (待续...)
- 第一章 Java多线程技能
1.初步了解"进程"."线程"."多线程" 说到多线程,大多都会联系到"进程"和"线程".那么这两者 ...
- java从基础知识(十)java多线程(下)
首先介绍可见性.原子性.有序性.重排序这几个概念 原子性:即一个操作或多个操作要么全部执行并且执行的过程不会被任何因素打断,要么都不执行. 可见性:一个线程对共享变量值的修改,能够及时地被其它线程看到 ...
随机推荐
- RibbitMQ 实战教程
# RabbitMQ 实战教程 ## 1.MQ引言 ### 1.1 什么是MQ `MQ`(Message Quene) : 翻译为 `消息队列`,通过典型的 `生产者`和`消费者`模型,生产者不断向消 ...
- 关于int和Integer缓存(二):修改缓存大小
续上文: java中的基础数据类型长度是否取决于操作系统? 在一些语言中,数据类型的长度是和操作系统有关系的,比如c和c++: 但是在java中,java的基础类型长度都是固定的,都是4个字节.因为j ...
- Spring笔记(2)
一.AOP简介 1.概念: 面向切面编程(Aspect-Oriented Programming),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善. ...
- AndroidJetpack Fragment之Navigation和ViewPager2
新的Fragment导航方式:Navigation 1.创建若干个fragment 2.添加导航 1)新建Navigation:右键res文件夹,New->Android Resource Fi ...
- sublime text 的 Ctrl + P「模糊搜索算法」
Reverse Engineering Sublime Text's Fuzzy Match 这是我能 google 到的最早的一篇关于 Sublime Text 的模糊搜索的文章. https:// ...
- Android Parsing between JSON and Kotlin Object with Google Gson Library
Parsing between JSON and Kotlin Object with Google Gson Library dependencies { ... implementation 'c ...
- 人生重开模拟器「GitHub 热点速览 v.21.36」
作者:HelloGitHub-小鱼干 人生是不能重来的,但是 lifeRestart 能满足你的重开心愿.初始值不满意,你可以一直随机生成或者自动添加颜值.智力.运气值,倒是一种"重生&qu ...
- Windows系统一些好用的办公工具
在日常办公过程中,总有一些工具令人觉得方便,提高了工作效率.以下是根据我的习惯,收集了一些好用的工具,在此记录且不定期更新. 文件名 说明 Everything 文件搜索工具,搜索速度快 ALTRun ...
- C语言中动态内存分配的本质是什么?
摘要:C语言中比较重要的就是指针,它可以用来链表操作,谈到链表,很多时候为此分配内存采用动态分配而不是静态分配. 本文分享自华为云社区<[云驻共创]C语言中动态内存分配的本质>,作者: G ...
- IPSec协议框架
文章目录 1. IPSec简介 1.1 起源 1.2 定义 1.3 受益 2. IPSec原理描述 2.1 IPSec协议框架 2.1.1 安全联盟 2.1.2 安全协议 报文头结构 2.1.3 封装 ...