Java——多线程高并发系列之wait()notify()notifyAll()interrupt()
Posted 张起灵-小哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java——多线程高并发系列之wait()notify()notifyAll()interrupt()相关的知识,希望对你有一定的参考价值。
文章目录:
Demo1(不在同步代码块中调用wait方法,则产生 java.lang.IllegalMonitorStateException 运行时异常)
Demo2(调用wait方法会使执行当前代码的线程进入等待状态)
Demo3(notify方法会唤醒之前执行wait方法等待的线程)
Demo8(notify方法唤醒过早,可能会打乱程序正常的执行逻辑)
Demo9(notify方法如果唤醒过早,那就不需要让线程继续等待了)
写在前面
首先需要说一下:wait()、notify()、notifyAll() 这三个方法并不是线程类中的方法,而是Object类中的方法,也就是说每个对象都有这三个方法。而interrupt()才是线程类中的方法。
Object 类中的 wait()方法可以使执行当前代码的线程等待,暂停执行,直到接到通知或被中断为止.
注意: ① wait()方法只能在同步代码块中由锁对象调用。②调用 wait()方法,当前线程会释放锁。
Object 类的 notify()可以唤醒线程,该方法也必须在同步代码块中由 锁 对 象 调 用。没 有 使 用 锁 对 象 调 用 wait()/notify() 会 抛 出 IlegalMonitorStateExeption 异常。如果有多个等待的线程,notify()方法只能唤醒其中的一个,在同步代码块中调用 notify()方法后,并不会立即释放锁对象,需要等当前同步代码块执行完后才会释放锁对象,一般将 notify()方法放在同步代码块的最后。
下面我给出自己写好的一些代码案例,来帮助大家更好的理解这四个方法的使用,因为代码中已经包含了必要的注释信息,所以每个Demo下面我就只附上代码和运行结果图了。。。
Demo1(不在同步代码块中调用wait方法,则产生 java.lang.IllegalMonitorStateException 运行时异常)
package com.szh.wait;
/**
* wait()方法是Object类中的方法,是每一个对象都具有的方法
* 调用wait()方法的代码块必须放在同步代码块中
* 否则会产生 java.lang.IllegalMonitorStateException 异常
*/
public class Test01 {
public static void main(String[] args) {
String test="szh";
try {
test.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Demo2(调用wait方法会使执行当前代码的线程进入等待状态)
package com.szh.wait;
/**
* wait()方法会使调用它的线程进行等待
* test对象调用了wait()方法,那么synchronized同步代码块中的锁对象就应该是test
* 否则程序会产生运行时异常 java.lang.IllegalMonitorStateException
*/
public class Test02 {
public static void main(String[] args) {
String test="szh";
System.out.println("同步前的代码...");
synchronized (test) {
System.out.println("同步代码块开始...");
try {
//调用wait方法后,当前线程就会等待,同时释放test锁对象
//当前线程需要被唤醒,如果没有唤醒就会一直等待
test.wait();
System.out.println("wait方法后面的代码...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main线程后面的代码...");
}
}
Demo3(notify方法会唤醒之前执行wait方法等待的线程)
package com.szh.wait;
/**
* 需要notify()方法唤醒等待的线程
*/
public class Test03 {
public static void main(String[] args) {
String test="szh";
//wait()
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
synchronized (test) {
System.out.println("线程t1开始等待:" + System.currentTimeMillis());
try {
//t1线程这里执行wait方法之后,会立刻释放它占有的test对象的锁
test.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程t1结束等待:" + System.currentTimeMillis());
}
}
});
//定义t2线程,负责唤醒t1线程
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
synchronized (test) {
System.out.println("线程t2开始唤醒:" + System.currentTimeMillis());
//t2线程这里执行notify方法之后,并不会立刻释放它占有的test对象的锁,而是等到同步代码块执行完才会释放
test.notify();
System.out.println("线程t2结束唤醒:" + System.currentTimeMillis());
}
}
});
t1.start(); //开启t1线程,t1开始等待
try {
Thread.sleep(1000 * 3); //main主线程睡眠3秒,确保t1进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start(); //t1线程开启3秒后,再开启t2线程,让它唤醒t1线程
}
}
Demo4(wait() & notify())
package com.szh.wait;
import java.util.ArrayList;
import java.util.List;
/**
* wait() & notify()
*/
public class Test04 {
public static void main(String[] args) {
//定义一个List集合存储String数据
List<String> list=new ArrayList<>();
//定义第一个线程,当list集合中的元素数量不等于5时,该线程等待
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
synchronized (list) {
if (list.size() != 5) {
System.out.println("线程t1开始等待:" + System.currentTimeMillis());
try {
//此时线程t1进入等待状态,会立刻释放它占有的list锁对象,然后线程t2执行
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程t1被唤醒了,结束等待:" + System.currentTimeMillis());
}
}
}
});
//定义第二个线程,想list集合中存储元素,当list集合中元素数量为5时,唤醒t1线程
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
synchronized (list) {
for (int i = 0; i < 10; i++) {
list.add("data ---> " + i);
System.out.println("线程t2向list集合中添加了第" + (i+1) + "个元素");
if (list.size() == 5) {
//当向list集合中添加了5个元素之后,其size等于5,这时候线程t2执行唤醒线程t1的操作
//但是此时,线程t2并不会立刻释放它占有的list锁对象,而是等这个同步代码块全部执行完毕,才会释放
//执行完毕的时候,线程t2已经向list集合中添加了10个元素,此时才会释放list锁对象,之后程序会转到28行执行
list.notify();
System.out.println("线程t2发出了唤醒线程t1的通知...");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
t1.start();
try {
//为了确保t2在t1之后执行,让t1先等待,这里先让main线程睡眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
Demo5(interrupt())
package com.szh.wait;
/**
* interrupt()方法会中断 wait() 的等待
* wait() 方法的中断会产生 InterruptedException 异常
* 中断之后,该线程也会释放锁对象
*/
public class Test05 {
//定义常量作为锁对象
private static final Object OBJ=new Object();
public static void main(String[] args) {
SubThread t=new SubThread();
t.start();
try {
//main主线程睡眠2秒,确保子线程t储与wait等待状态
Thread.sleep(1000 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程t执行中断操作
t.interrupt();
}
static class SubThread extends Thread {
@Override
public void run() {
synchronized (OBJ) {
System.out.println("begin wait...");
try {
OBJ.wait();
} catch (InterruptedException e) {
System.out.println("wait方法的等待被中断了...");
}
}
}
}
}
Demo6(notifyAll())
package com.szh.wait;
/**
* notify() & notifyAll()
* notify() 一次只能唤醒一个线程,如果有多个等待的线程,只能随机唤醒其中的某一个;
* 想要唤醒所有等待线程,需要调用 notifyAll()
*/
public class Test06 {
public static void main(String[] args) {
Object obj=new Object();
SubThread t1=new SubThread(obj);
SubThread t2=new SubThread(obj);
SubThread t3=new SubThread(obj);
t1.setName("t1");
t2.setName("t2");
t3.setName("t3");
t1.start();
t2.start();
t3.start();
try {
Thread.sleep(1000 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {
/*
调用一次 notify() 只能唤醒其中的一个线程,其他等待的线程依然处于等待状态,
对于处于等待状态的线程来说,错过了通知信号,这种现象也称为信号丢失
*/
//lock.notify();
//唤醒所有的线程
obj.notifyAll();
}
}
static class SubThread extends Thread {
private Object obj;
public SubThread(Object obj) {
this.obj = obj;
}
@Override
public void run() {
synchronized (obj) {
System.out.println(Thread.currentThread().getName() + " ---> begin wait...");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ---> end wait...");
}
}
}
}
Demo7(wait(long))
package com.szh.wait;
/**
* wait(long): 带有 long 类型参数的 wait()等待,
* 如果在参数指定的时间内没有被唤醒,超时后会自动唤醒.
*/
public class Test07 {
private static final Object OBJ=new Object();
public static void main(String[] args) {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
synchronized (OBJ) {
System.out.println("线程t开始等待...");
try {
//如果3000毫秒内,线程t没有被唤醒,则线程t会自动唤醒
OBJ.wait(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程t结束等待...");
}
}
});
t.start();
}
}
Demo8(notify方法唤醒过早,可能会打乱程序正常的执行逻辑)
package com.szh.wait;
/**
* 线程 wait()等待后,可以调用 notify()唤醒线程,
* 如果 notify()唤醒的过早,在等待之前就调用了 notify() 可能会打乱程序正常的运行逻辑.
*/
public class Test08 {
public static void main(String[] args) {
final Object Lock=new Object();
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
synchronized (Lock) {
System.out.println("begin wait...");
try {
Lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end wait...");
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
synchronized (Lock) {
System.out.println("begin notify...");
Lock.notify();
System.out.println("end notify...");
}
}
});
//如果先开启 t1 线程,再开启 t2 线程, 大多数情况下, t1 先等待, 之后 t2 再把 t1 唤醒,程序正常执行
//t1.start();
//t2.start();
//如果先开启 t2 通知线程, 再开启 t1 等待线程, 可能会出现 t1 线程一直等待(因为t2线程已经唤醒过了,不会再次唤醒了),
//所以t1线程就没有收到 t2 线程唤醒它的通知,t1线程就会一直等待下去
t2.start();
t1.start();
//调用start方法的顺序不一定就是线程实际开启的顺序
}
}
Demo9(notify方法如果唤醒过早,那就不需要让线程继续等待了)
package com.szh.wait;
/**
* notify()通知过早, 就不让线程继续等待了
*/
public class Test09 {
static boolean isFirst=true;
public static void main(String[] args) {
final Object Lock=new Object();
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
synchronized (Lock) {
while (isFirst) { //当线程t1是第一个开启的线程时,就等待
System.out.println("begin wait...");
try {
Lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end wait...");
}
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
synchronized (Lock) {
System.out.println("begin notify...");
Lock.notify();
System.out.println("end notify...");
isFirst=false; //线程t2通知后,就把第一个线程的标志修改为false
}
}
});
//如果先开启 t1 线程,再开启 t2 线程, 大多数情况下, t1 先等待, 之后 t2 再把 t1 唤醒
// t1.start();
// t2.start();
//如果先开启 t2 通知线程, 再开启 t1 等待线程, 可能会出现 t1 线程一直等待,没有收到 t2 线程唤醒它的通知
t2.start();
t1.start();
//调用start方法的顺序不一定就是线程实际开启的顺序
}
}
Demo10(wait等待条件发生了变化)
package com.szh.wait;
import java.util.ArrayList;
import java.util.List;
/**
* wait 条件发生变化
* 定义一个集合
* 定义一个线程向集合中添加数据, 添加完数据后通知另外的线程从集合中取数据
* 定义一个线程从集合中取数据, 如果集合中没有数据就等待
*/
public class Test10 {
public static void main(String[] args) {
//定义添加数据的线程对象
ThreadAdd threadAdd=new ThreadAdd();
threadAdd.setName("threadAdd");
//定义取数据的线程对象
ThreadSubtract threadSubtract=new ThreadSubtract();
threadSubtract.setName("threadSubtract");
//测试一:先开启添加数据的线程,再开启取数据的线程,大多数情况下会正常的存取数据
// threadAdd.start();
// threadSubtract.start();
//测试二:先开启取数据的线程,再开启添加数据的线程,取数据的线程会先等待,等到添加数据之后,再取数据
// threadSubtract.start();
// threadAdd.start();
//测试三: 开启两个取数据的线程,再开启添加数据的线程,此时运行会产生 java.lang.IndexOutOfBoundsException 异常
ThreadSubtract threadSubtract2 = new ThreadSubtract();
threadSubtract2.setName("threadSubtract2");
threadSubtract.start();
threadSubtract2.start();
threadAdd.start();
}
//1.定义一个集合
static List<String> list=new ArrayList<>();
//2.定义方法,从集合中取数据
public static void subtract() {
synchronized (list) {
while (list.size() == 0) {
System.out.println(Thread.currentThread().getName() + " begin wait...");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end wait...");
}
Object obj=list.remove(0);
System.out.println(Thread.currentThread().getName() + "从集合中取了" + obj + "后,集合中数据的数量:" + list.size());
}
}
//3.定义方法,向集合中添加数据
public static void add() {
synchronized (list) {
list.add("data");
System.out.println(Thread.currentThread().getName() + "向集合中存储了一个数据");
list.notifyAll();
}
}
//4.定义线程类调用 add() 取数据的方法
static class ThreadAdd extends Thread {
@Override
public void run() {
add();
}
}
//5.定义线程类调用 subtract() 方法
static class ThreadSubtract extends Thread {
@Override
public void run() {
subtract();
}
}
}
以上是关于Java——多线程高并发系列之wait()notify()notifyAll()interrupt()的主要内容,如果未能解决你的问题,请参考以下文章
Java——多线程高并发系列之Condition接口中的await()signal()signAll()方法
Java——多线程高并发系列之Condition接口中的await()signal()signAll()方法
Java——多线程高并发系列之创建多线程的三种方式(ThreadRunnableCallable)
Java——多线程高并发系列之创建多线程的三种方式(ThreadRunnableCallable)