线程通知和等待
Java 中的 Object 类是所有类的父类,鉴于继承机制, Java 把所有类都需要的方法放Object 类里面,下面看下Object 类中和线程相关的方法:
public class Object {
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
public final void wait() throws InterruptedException {
wait(0);
}
}
上面Object中线程的方法都需要在获取到对象的监视器锁中使用,否者线程会抛出 IllegalMonitorStateException 异常。
先简单说一下监视器锁,后面会有博文详细说明:
synchronized
执行 synchronized 步代码块时 使用该共享变量作为参数。
synchronized (共享变量){
//doSomething
}
调用该共享变量的方法,并且该方法使用了 synchronized 修饰
synchronized void add (int a , int b) {
//doSomething
}
wait
当一个线程调用一个共享变量 wait()方法时, 该调用线程会被阻塞挂起, 到发生下面几件事情才返回
- 其他线程调用了该共享对象 notify()或者 notifyAll()方法;
- 其他线程调用了该线程 interrupt()方法,导致该线程抛出 InterruptedException 异常返回。
虚假唤醒
一个线程可以从挂起状态变为可以运行状态(就是被唤醒),即使该线程没有被其他线程调用 notify()、 notifyAll()方法进行通知,或者被中断,或者等待超时,这就是所谓的虚假唤醒。
要怎么避免虚假唤醒呢?
synchronized (obj) {
while (条件不满足) {
obj.wait();
}
}
线程等待不释放锁
继续来看一个例子:
当前线程调用共享变量的 wait()方法后只会释放当前共享变量上的锁,如果当前线程还持有其他共享变量的锁,则这些锁是不会被释放的。
public class WaitNotifyTest {
/**
* 资源A
*/
private static volatile Object resourceA = new Object();
/**
* 资源B
*/
private static volatile Object resourceB = new Object();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (resourceA) {
// 获取resourceA共享资源的监视器锁
System.out.println("threadA 获取到 resourceA 监视器锁");
// 获取resourceB共享资源的监视器锁
synchronized (resourceB) {
System.out.println("threadA 获取到 resourceB 监视器锁");
// 线程A阻塞, 并释放获取到的resourceA锁
System.out.println("threadA 释放 resourceA 监视器锁");
resourceA.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
// 睡眠1s
Thread.sleep(1000);
synchronized (resourceA) {
// 获取resourceA共享资源的监视器锁
System.out.println("threadB 获取到 resourceA 监视器锁");
System.out.println("threadB 尝试获取 resourceB 监视器锁...");
// 获取resourceB共享资源的监视器锁
synchronized (resourceB) {
System.out.println("threadB 获取到 resourceB 监视器锁");
// 线程B阻塞, 并释放获取到的resourceA锁
System.out.println("threadB 释放 resourceA 监视器锁");
resourceA.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadA.start();
threadB.start();
threadA.join();
threadB.join();
System.out.println("main over");
}
}
threadA 获取到 resourceA 监视器锁
threadA 获取到 resourceB 监视器锁
threadA 释放 resourceA 监视器锁
threadB 获取到 resourceA 监视器锁
threadB 尝试获取 resourceB 监视器锁...
由上面代码测试结果可以看出: 当前线程调用共享对象的wait()方法时, 当前线程只会释放当前共享对象的锁, 当前线程持有的其他共享对象的监视器锁不会被释放掉!
中断等待中的线程
当一个线程调用共享对象的wait() 方法被阻塞挂起后, 如果其他线程中断了该线程, 则会抛出InterruptedException异常并返回
public class WaitNotifyInterrupt {
static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("----begin----");
synchronized (obj) {
obj.wait();
}
System.out.println("----end----");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadA.start();
Thread.sleep(1000);
System.out.println("----begin interrupt threadA----");
threadA.interrupt();
System.out.println("----end interrupt threadA----");
}
}
----begin----
----begin interrupt threadA----
----end interrupt threadA----
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at base.thread.book.WaitNotifyInterrupt$1.run(WaitNotifyInterrupt.java:19)
at java.lang.Thread.run(Thread.java:748)
notify
一个线程调用共享对象的 notify()方法后,会唤醒一个在该共享变量上调用wait系列方法后被挂起的线程。一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的。
此外,被唤醒的线程不能马上从 wait 方法返回并继续执行,它必须在获取了共享对象的监视器锁后才可以返回,也就是唤醒它的线程释放了共享变量上的监视器锁后,被唤醒的线程也不一定会获取到共享对象的监视器锁,这是因为该线程还需要和其他线程一起竞争该锁,只有该线程竞争到了共享变量的监视器锁后才可以继续执行。
/**
* 资源A
*/
private static volatile Object resourceA = new Object();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
// 获取resourceA共享资源的监视器锁
synchronized (resourceA) {
System.out.println("threadA get resourceA lock");
try {
System.out.println("threadA start wait");
resourceA.wait();
System.out.println("threadA end wait");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA) {
System.out.println("threadB get resourceA lock");
try {
System.out.println("threadB start wait");
resourceA.wait();
System.out.println("threadB end wait");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread threadC = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA) {
System.out.println("threadC begin notify");
resourceA.notify();
}
}
});
threadA.start();
threadB.start();
Thread.sleep(1000);
threadC.start();
threadA.join();
threadB.join();
threadC.join();
System.out.println("main over");
}
threadA get resourceA lock
threadA start wait
threadB get resourceA lock
threadB start wait
threadC begin notify
threadA end wait
从代码执行结果可以看出:threadB 线程还在等待中,threadA 线程已经执行结束了。
notifyAll
不同于在共享变量上调用 notify()函数会唤醒被阻塞到该共享变量上的一个线程, notifyAll()方法则会唤醒所有在该共享变量上由于调用 wait 系列方法而被挂起的线程。
/**
* 资源A
*/
private static volatile Object resourceA = new Object();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
// 获取resourceA共享资源的监视器锁
synchronized (resourceA) {
System.out.println("threadA get resourceA lock");
try {
System.out.println("threadA start wait");
resourceA.wait();
System.out.println("threadA end wait");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA) {
System.out.println("threadB get resourceA lock");
try {
System.out.println("threadB start wait");
resourceA.wait();
System.out.println("threadB end wait");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread threadC = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA) {
System.out.println("threadC begin notify");
resourceA.notifyAll();
}
}
});
threadA.start();
threadB.start();
Thread.sleep(1000);
threadC.start();
threadA.join();
threadB.join();
threadC.join();
System.out.println("main over");
}
threadA get resourceA lock
threadA start wait
threadB get resourceA lock
threadB start wait
threadC begin notify
threadB end wait
threadA end wait
main over
从代码执行结果看:threadA 和 threadB 都执行结束了。
本文地址:https://blog.csdn.net/weixin_45847167/article/details/112611672
推荐阅读
-
10_Android中通过HttpUrlConnection访问网络,Handler和多线程使用,读取网络html代码并显示在界面上,ScrollView组件的使用_html/css_WEB-ITnose
-
线程高级篇-Lock锁和Condition条件
-
4核8线程和6核6线程哪个比较好?CPU核心多和线程多的区别
-
Java多线程之synchronized和lock
-
CURL的学习和应用(附多线程实现)
-
线程的等待与通知,如何使用Condition实现?
-
java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)
-
java并发编程(十三)- 显示锁使用Lock和Condition实现等待通知模式
-
20 - Lock-Condition 的等待通知
-
【多线程】Lock中的Condition实现等待通知