线程是抢占式执行的,无法预知线程之间的执行顺序。线程是随机调度。 但有时程序员也希望能合理协调多个线程的执行顺序。 因此,在 Java 中使用了等待(wait)和通知(notify)机制,用于在应用层面上干预多个线程的执行顺序。
完成这个协调⼯作, 主要涉及到三个⽅法
注意:wait,notify,notifyAll都是Object类的方法
• 使当前执⾏代码的线程进⾏等待.(把线程放到等待队列中) • 释放当前的锁 • 满⾜⼀定条件时被唤醒,重新尝试获取这个锁.
wait要搭配synchronized来使⽤.脱离synchronized使⽤wait会直接抛出异常.
代码⽰例:观察wait()⽅法使⽤:
public class demo4 {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
synchronized (object) {
System.out.println("等待中");
object.wait();
System.out.println("等待结束");
}
}
}
这样在执⾏到object.wait()之后就⼀直等待下去,那么程序肯定不能⼀直这么等待下去了
public class demo4 {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
synchronized (object) {
System.out.println("等待中");
object.wait(1000);
System.out.println("等待结束");
}
}
}
等待中
等待结束
Process finished with exit code 0
public class demo4 {
public static void main(String[] args) {
//创建锁对象;
Object locker = new Object();
Thread t1 = new Thread(() -> {
System.out.println("wait前");
//在 synchronized 代码块中调用 wait 方法;
synchronized (locker) {
// wait 方法是由锁对象调用的,调用后,线程释放锁,进入等待队列;
try {
locker.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("wait后");
});
t1.start();
//抛出异常,清除中断标志,唤醒t1线程。
t1.interrupt();
}
}
4)使用notify方法唤醒
notify⽅法是唤醒等待的线程.
public class demo4 {
public static void main(String[] args) {
//创建锁对象;
Object locker = new Object();
Thread t1 = new Thread(()->{
System.out.println("t1 wait前!");
synchronized (locker) {
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("t1 wait后!");
});
Thread t2 = new Thread(()-> {
try {
Thread.sleep(1000);
System.out.println("t2 notify 之前");
//让 t1 线程有时间进入到wait状态
synchronized (locker) {
locker.notify();
System.out.println("t2 notify 之后");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t1.start();
t2.start();
}
}
t1 wait前!
t2 notify 之前
t2 notify 之后
t1 wait后!
Process finished with exit code 0
使用notifyAll()⽅法 可以唤起所有的线程
public static void main(String[] args) throws InterruptedException {
Object locker = new Object();
Thread t1 = new Thread(()->{
System.out.println("t1 wait!");
synchronized (locker) {
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("t1 over!");
});
Thread t2 = new Thread(()->{
System.out.println("t2 wait!");
synchronized (locker) {
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("t2 over!");
});
Thread t3 = new Thread(()->{
System.out.println("t3 wait!");
synchronized (locker) {
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("t3 over!");
});
Thread t4 = new Thread(()->{
synchronized (locker) {
System.out.println("t4 唤起所有线程");
locker.notifyAll();
}
});
t1.start();
t2.start();
t3.start();
Thread.sleep(1000);
t4.start();
}
添加
Thread.sleep(1000)
后,程序会稍微暂停 1 秒钟,确保t1
、t2
和t3
线程已经进入wait()
方法并且已经释放了locker
锁。这样,在t4
线程调用notifyAll()
时,locker
锁已经被释放,t4
可以成功获取锁并调用notifyAll()
来唤醒所有等待的线程,从而避免了死锁。
特性 | wait() | sleep() |
---|---|---|
来源 | Object 类的实例方法 | Thread 类的静态方法 |
锁的释放 | 会释放对象锁 | 不释放锁 |
线程状态 | 进入等待状态(Waiting) | 进入休眠状态(Sleeping) |
使用场景 | 线程间通信与协调(如生产者-消费者) | 控制线程暂停时间、模拟延时等 |
中断处理 | 抛出 InterruptedException | 抛出 InterruptedException |
参数 | 可以指定等待时间(毫秒、纳秒) | 可以指定休眠时间(毫秒、纳秒) |
是否需要同步 | 需要在同步代码块或同步方法中调用 | 不需要同步 |
影响线程调度 | 影响线程的调度,直到线程被唤醒 | 影响线程调度,直到指定时间过后 |
线程恢复 | 通过 notify() 或 notifyAll() 唤醒 | 休眠时间到达后自动恢复 |
总结:
wait()
是 Object
类的一个方法,它必须在同步块或同步方法中调用,用于释放当前线程持有的锁,并使线程进入等待队列,直到其他线程调用同一对象的 notify()
或 notifyAll()
方法。而 sleep()
是 Thread
类的静态方法,线程调用时不需要持有锁,它会让当前线程休眠指定的时间。
wait()
会释放对象的锁,而 sleep()
不会释放锁。
wait()
主要用于线程间的通信,线程进入等待状态直到被唤醒;sleep()
主要用于控制线程的暂停时间,通常不用于线程间的协调。
结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!