Object的wait/notify/notifyAll&&Thread的sleep/yield/join/holdsLock
一、wait/notify/notifyAll都是Object类的实例方法
1、wait方法:阻塞当前线程等待notify/notifyAll方法的唤醒,或等待超时后自动唤醒。
wait等待其实是对象monitor
JDK中提供了三个重载方法,
(1)void wait()方法的作用是将当前运行的线程挂起(即让其进入阻塞状态),不再占据CPU,直到notify或notifyAll方法来唤醒线程.
(2)void wait(long timeout),该方法与wait()方法类似,唯一的区别就是在指定时间内,如果没有notify或notifAll方法的唤醒,也会自动唤醒。
(3)void wait(long timeout,long nanos),本意在于更精确的控制调度时间,不过从目前版本来看,该方法貌似没有完整的实现该功能。
注意:wait(0);// timeout 为零时,则不考虑实际时间,在获得通知前该线程将一直等待;和wait()是一样的含义。实际wait()的实现就一句代码:wait(0)
我们写一段代码实现:程序执行以后,暂停一秒,然后再执行。
package com.paddx.test.concurrent; public class WaitTest { public void testWait(){
System.out.println("Start-----");
try {
wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End-------");
} public static void main(String[] args) {
final WaitTest test = new WaitTest();
new Thread(new Runnable() {
@Override
public void run() {
test.testWait();
}
}).start();
}
}
运行上述代码,查看结果:
Start-----
Exception in thread
"Thread-0"
java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at com.paddx.test.concurrent.WaitTest.testWait(WaitTest.java:
8
)
at com.paddx.test.concurrent.WaitTest$
1
.run(WaitTest.java:
20
)
at java.lang.Thread.run(Thread.java:
745
)
Thrown to indicate that a thread has attempted to wait on an object
's monitor or to notify other threads waiting on an object'
s monitor without owning the specified monitor.
这句话的意思大概就是:线程 试图等待对象的监视器 或者 试图通知其他正在等待对象监视器 的线程,但本身没有对应的监视器的所有权。
这个问题在《synchronized底层实现原理》一文中有提到过,wait方法是一个本地方法,其底层是通过一个叫做监视器锁的对象来完成的。所以上面之所以会抛出异常,是因为在调用wait方式时没有获取到monitor对象的所有权。
那如何获取monitor对象所有权?Java中只能通过synchronized关键字来完成,修改上述代码,增加Synchronized关键字:
package com.paddx.test.concurrent; public class WaitTest { public synchronized void testWait(){//增加Synchronized关键字
System.out.println("Start-----");
try {
wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End-------");
} public static void main(String[] args) {
final WaitTest test = new WaitTest();
new Thread(new Runnable() {
@Override
public void run() {
test.testWait();
}
}).start();
}
}
现在再运行上述代码,就能看到预期的效果了:
Start-----
End-------
于是通过wait(long timeout)方法在单线程中,便实现了与Thread.sleep(long millis)一样的效果2、notify/notifyAll方法
(1)void notify() :Wakes up a single thread that is waiting on this object's monitor. 有多个线程在等待同一个object的monitor时,仅仅唤醒其中的一个线程(随机选择, 或者说是竞争到锁的那一个)
(2)void notifyAll():Wakes up all threads that are waiting on this object's monitor. 有多个线程在等待同一个object的monitor时,会唤醒全部的这些线程(按照竞争到锁的顺序逐个唤醒)
wait/notify/notifyAll是通过对象的monitor对象来实现的,只要在同一对象上去调用notify/notifyAll方法,就可以唤醒对应对象monitor上等待的线程了
看下面的例子来理解这两者的差别:
package com.paddx.test.concurrent; public class NotifyTest {
public synchronized void testWait(){
System.out.println(Thread.currentThread().getName() +" Start-----");
try {
wait(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +" End-------");
} public static void main(String[] args) throws InterruptedException {
final NotifyTest test = new NotifyTest();
for(int i=0;i<5;i++) {
new Thread(new Runnable() {
@Override
public void run() {
test.testWait();
}
}).start();
} Thread.sleep(1000);
synchronized (test) {
test.notify();
}
Thread.sleep(3000);
System.out.println("-----------分割线-------------"); synchronized (test) {
test.notifyAll();
}
}
}
输出结果如下:
Thread-
0
Start-----
Thread-
1
Start-----
Thread-
2
Start-----
Thread-
3
Start-----
Thread-
4
Start-----
Thread-
0
End-------
-----------分割线-------------
Thread-
4
End-------
Thread-
3
End-------
Thread-
2
End-------
Thread-
1
End-------
最后,有两点点需要注意:
(1)调用wait方法后,线程是会释放对monitor对象的所有权。
(2)一个通过wait方法阻塞的线程,必须同时满足以下两个条件才能被真正执行:
- 线程需要被唤醒(超时唤醒或调用notify/notifyAll)
- 线程唤醒后需要竞争到锁(monitor)
注意: notifyAll时,如果多个线程在wait,此时多个线程需要对锁进行竞争;竞争到锁的线程获得执行,等该线程释放锁后,剩下的线程再次竞争;如此重复,直到上一轮wait的线程都得到一次锁得以执行,否则一旦有线程释放了锁,未曾得到过锁的线程还会得到通知。
二、Thread类的sleep/yield/join方法:sleep和yield是静态方法,join是实例方法
sleep/join的底层实现也使用了synchronized /wait等机制或方法
1、sleep
先看一下实现源码:
public static void sleep(long millis) throws InterruptedException {
Thread.sleep(millis, 0);
} public static void sleep(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("millis < 0: " + millis);
}
if (nanos < 0) {
throw new IllegalArgumentException("nanos < 0: " + nanos);
}
if (nanos > 999999) {
throw new IllegalArgumentException("nanos > 999999: " + nanos);
} // The JLS 3rd edition, section 17.9 says: "...sleep for zero
// time...need not have observable effects."
if (millis == 0 && nanos == 0) {
// ...but we still have to handle being interrupted.
if (Thread.interrupted()) {
throw new InterruptedException();
}
return;
} long start = System.nanoTime();
long duration = (millis * NANOS_PER_MILLI) + nanos; Object lock = currentThread().lock; // Wait may return early, so loop until sleep duration passes.
synchronized (lock) {//还是使用了锁
while (true) {
sleep(lock, millis, nanos); long now = System.nanoTime();
long elapsed = now - start; if (elapsed >= duration) {
break;
} duration -= elapsed;
start = now;
millis = duration / NANOS_PER_MILLI;
nanos = (int) (duration % NANOS_PER_MILLI);
}
}
} @FastNative
private static native void sleep(Object lock, long millis, int nanos)
throws InterruptedException;
sleep方法的作用是让当前线程暂停指定的时间(毫秒)。唯一需要注意的是其与wait方法的区别。
1)wait方法依赖于同步,而sleep方法可以直接调用。
2)sleep方法只是暂时让出CPU的执行权,并不释放锁。而wait方法则需要释放锁。(看下面的例子理解)
第2)个区别可以理解 调用 Thread.sleep(0) 的作用:进行一次CPU使用权的重新争夺
package com.paddx.test.concurrent; public class SleepTest {
public synchronized void sleepMethod(){
System.out.println("Sleep start-----");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Sleep end-----");
} public synchronized void waitMethod(){
System.out.println("Wait start-----");
synchronized (this){
try {
wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Wait end-----");
} public static void main(String[] args) {
final SleepTest test1 = new SleepTest(); for(int i = 0;i<3;i++){
new Thread(new Runnable() {
@Override
public void run() {
test1.sleepMethod();
}
}).start();
} try {
Thread.sleep(10000);//暂停十秒,等上面程序执行完成
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-----分割线-----"); final SleepTest test2 = new SleepTest(); for(int i = 0;i<3;i++){
new Thread(new Runnable() {
@Override
public void run() {
test2.waitMethod();
}
}).start();
} }
}
执行结果:
Sleep start-----
Sleep end-----
Sleep start-----
Sleep end-----
Sleep start-----
Sleep end-----
-----分割线-----
Wait start-----
Wait start-----
Wait start-----
Wait end-----
Wait end-----
Wait end-----
package com.paddx.test.concurrent; public class YieldTest implements Runnable {
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName() + ": " + i);
Thread.yield();
}
} public static void main(String[] args) {
YieldTest runn = new YieldTest();
Thread t1 = new Thread(runn,"FirstThread");
Thread t2 = new Thread(runn,"SecondThread"); t1.start();
t2.start(); }
}
运行结果如下:
FirstThread:
0
SecondThread:
0
FirstThread:
1
SecondThread:
1
FirstThread:
2
SecondThread:
2
FirstThread:
3
SecondThread:
3
FirstThread:
4
SecondThread:
4
源码中的说明主要说了三个问题:
- 调度器可能会忽略该方法。
- 使用的时候要仔细分析和测试,确保能达到预期的效果。
- 很少有场景要用到该方法,主要使用的地方是调试和测试。
public final void join(long millis, int nanos)
throws InterruptedException {
synchronized(lock) {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
} if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
} if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
} join(millis);
}
} public final void join() throws InterruptedException {
join(0);
} public final void join(long millis) throws InterruptedException {
synchronized(lock) {//使用了锁
long base = System.currentTimeMillis();
long now = 0; if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
} if (millis == 0) {
while (isAlive()) {
lock.wait(0);//调用了wait
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
lock.wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
}
void join():Waits for this thread to die.
比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
t.join(); //调用join方法,等待线程t执行完毕
t.join(1000); //等待 t 线程,等待时间是1000毫秒
下面我们通过一个例子来演示join方法的作用:
(1)不使用join方法:
package com.paddx.test.concurrent; public class JoinTest implements Runnable{
@Override
public void run() { try {
System.out.println(Thread.currentThread().getName() + " start-----");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " end------");
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
for (int i=0;i<5;i++) {
Thread test = new Thread(new JoinTest());
test.start();
} System.out.println("Finished~~~");
}
}
执行结果如下:
Thread-
0
start-----
Thread-
1
start-----
Thread-
2
start-----
Thread-
3
start-----
Finished~~~
Thread-
4
start-----
Thread-
2
end------
Thread-
4
end------
Thread-
1
end------
Thread-
0
end------
Thread-
3
end------
(2)使用join方法:
package com.paddx.test.concurrent; public class JoinTest implements Runnable{
@Override
public void run() { try {
System.out.println(Thread.currentThread().getName() + " start-----");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " end------");
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
for (int i=0;i<5;i++) {
Thread test = new Thread(new JoinTest());
test.start();
try {
test.join(); //调用join方法
} catch (InterruptedException e) {
e.printStackTrace();
}
} System.out.println("Finished~~~");
}
}
执行结果如下:
Thread-
0
start-----
Thread-
0
end------
Thread-
1
start-----
Thread-
1
end------
Thread-
2
start-----
Thread-
2
end------
Thread-
3
start-----
Thread-
3
end------
Thread-
4
start-----
Thread-
4
end------
Finished~~~
public static boolean holdsLock(Object obj) //判断当前线程是否拥有obj的锁(Monitor)
Object的wait/notify/notifyAll&&Thread的sleep/yield/join/holdsLock的更多相关文章
- 使用Object的wait,notify,notifyAll做线程调度
我们知道java中的所有类的祖先都是Object,Object类有四个个方法wait(),wait(long timeout),notify(),notifyAll(),这四个方法可以用来做线程的调度 ...
- Java-JUC(九):使用Lock替换synchronized,使用Condition的await,singal,singalall替换object的wait,notify,notifyall实现线程间的通信
Condition: condition接口描述了可能会与锁有关的条件变量.这些用法上与使用object.wait访问隐式监视器类似,但提供了更强大的功能.需要特别指出的是,单个lock可能与多个Co ...
- -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait( ...
- 为什么notify(), wait()等函数定义在Object中,而不是Thread中
Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作. wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有 ...
- Object中的wait,notify,notifyAll基本使用(转)
让线程停止运行/睡眠的方法只有两个:Thread.sleep()或者obj.wait() 记住obj.nofity()并不能停止线程运行,因为notify虽然释放了锁,但依然会急促执行完synchro ...
- Java Object对象中的wait,notify,notifyAll的理解
wait,notify,notifyAll 是定义在Object类的实例方法,用于控制线程状态,在线程协作时,大家都会用到notify()或者notifyAll()方法,其中wait与notify是j ...
- java 多线程之wait(),notify,notifyAll(),yield()
wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能.因为都个对像都 ...
- java中的wait(),notify(),notifyAll(),synchronized方法
wait(),notify(),notifyAll()三个方法不是Thread的方法,而是Object的方法.意味着所有对象都有这三个方法,因为每个对象都有锁,所以自然也都有操作锁的方法了.这三个方法 ...
- java 多线程(wait/notify/notifyall)
package com.example; public class App { /* wait\notify\notifyAll 都属于object的内置方法 * wait: 持有该对象的线程把该对象 ...
随机推荐
- 将Excel导出为SQL语句
需求说明:公司做项目前进行需求分析,确定表结构后需要建表,如果照着表格去敲,那就太麻烦了,所以想到了自动生成SQL语句. 思路大概就是:解析Excel,拼接SQL语句,输出SQL文件. 第三方jar包 ...
- java equals和hashcode方法
equals()方法比较两个对象的引用是否相同 hashcode()方法比较两个对象的哈希码是否相同
- CodeForces1051E EXKMP + 线段树dp
http://codeforces.com/problemset/problem/1051/E 题意:给你一个很大的数字,然后你可以把这个数字拆分成为任意多个部分,要求每一个部分的数字大小要在一个区间 ...
- 富文本框vue-quill-editor的使用
vue-quill官网:https://www.npmjs.com/package/vue-quill-editor quill官网:https://quilljs.com/docs/quicksta ...
- docker mesos集群资源调度平台
mesos原理与架构 首先,再次需要强调 Mesos 自身只是一个资源调度框架,并非一整套完整的应用管理平台,所以只有 Mesos 自己是不能干活的.但是基于 Mesos,可以比较容易地为各种应用管理 ...
- NSGA-II入门C1
NSGA-II入门C1 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考文献1 参考文献2 白话多目标 多目标中的目标是个瓦特? 多目标即是优化问题中的优化目标在3个及以上,一般这些优化的 ...
- Kafka权威指南 读书笔记之(四)Kafka 消费者一一从 Kafka读取数据
KafkaConsumer概念 消费者和消费者群组 Kafka 消费者从属于消费者群组.一个群组里的消费者订阅的是同一个主题,每个消费者接收主题一部分分区的消息. 往群组里增加消费者是横向伸缩消费能力 ...
- Hive记录-Impala jdbc连接hive和kudu参考
1.配置环境Eclipse和JDK 2.加载hive jar包或者impala jar包 备注:从CDH集群里面拷贝出来 下载地址:https://www.cloudera.com/downloads ...
- NPOI使用教程附Helper
1 NPOI简介 1.1 NPOI是什么 NPOI是POI的.NET版本,POI是一套用Java写成的库,我们在开发中经常用到导入导出表格.文档的情况,NPOI能够帮助我们在没有安装微软Office的 ...
- hdu 6383
题意是说给定一个序列,能否通过任意次对部分数字 +1,对部分数字 -2的操作使得序列在满足全部非负且任意两元素的差值不超过1的前提下最小值最大,求最大值. 一开始的时候没有注意到整个序列全是非负数,还 ...