文章目录
- 💐wait() 讲解
- 💐notify() 讲解
- 💐notifyAll()
- 💡wait() 和 sleep() 的区别
首先,我们知道,线程的执行顺序是随机的(操作系统随机调度的,抢占式执行),但是有时候,我们并不喜欢这种随机的执行,更喜欢的是它们能够顺序的执行,所以,Java就引入了一种机制,wait() 和 notify() ,它们的 作用就是保证线程执行的顺序;
而前面的文章中也讲过一个方法 join(),也是能影响线程的执行顺序,但是呢,这个join只能控制线程结束的顺序,而我们想要的是,线程不结束,也能按照我们自己规定的顺序去依次执行;
💐wait() 讲解
使用 wait() 时要注意一定要搭配 synchronized 使用,否则的话就会抛出异常
调用 wait() 时,wait() 会做三件事:
- 使当前的线程处于阻塞等待
- 释放当前线程获取到的锁
- 在其他线程中使用锁对象调用notify时或者使用带参数的wait(带有时间参数,超过时间就会被唤醒)被唤醒后,会再重新尝试获取锁
public class Main {
public static void main(String[] args) {
Object locker = new Object();
Thread thread1 = new Thread(() -> {
synchronized (locker) {
System.out.println("调用wait(), 阻塞等待");
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("thread1被唤醒");
}
});
thread1.start();
}
}
可以看到,当执行到 wait() 这行代码时,就一直处于了阻塞等待,需要在其他线程中使用 notify() 来唤醒,而目前代码中没有其他线程,所以就一直等待;这里也需要解释一下,关于调用 wait() 方法,因为 wait() 方法是Object类中的方法,所以,所有的对象都可以调用;
💐notify() 讲解
notify 作用:唤 醒其它调用了 wait() 导致阻塞等待的线程;
1. wati() 和 notify() 都要在加锁的代码块中使用,并且由锁对象调用
2. 使用 notify() 唤醒某个线程时,只能唤醒和调用 notify() 是同一个锁的线程
比如:thread1 线程中使用 锁对象 locker1 调用了 wait() 方法阻塞等待,那么在其他线程中,也要使用 locker1 来调用 notify() 方法对 thread1 进行唤醒
public static void main(String[] args) throws InterruptedException {
Object locker = new Object();
Thread thread1 = new Thread(() -> {
synchronized (locker) {
System.out.println("thread1执行,调用wait,进行阻塞,同时锁被释放");
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("thread1执行完");
}
});
Thread thread2 = new Thread(() -> {
//先进行一个睡眠,可以明显的观察效果
try {
Thread.sleep(100);//让thread1先执行
System.out.println("thread2执行,进入睡眠四秒");
Thread.sleep(4000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (locker) {
System.out.println("四秒后");
System.out.println("进行唤醒");
locker.notify();
}
});
thread1.start();
thread2.start();
}
wait() 、notify() 也可以避免“线程饿死”
举个例子:
💐notifyAll()
如果多个线程都调用 wait() 的话,多个线程都会进入阻塞,调用 notify() 的话,就只能唤醒一个,但是用 notifyAll() 的话,就可以一次性全部唤醒(唤醒的是等待 同一个锁的所有线程),这里要注意一点:多个线程被同时唤醒时,只有一个线程可以成功的获取到锁,其他的线程进行继续等待。
场景:有四个线程,thread1,thread2,thread3 调用 wait() 进行阻塞等待,thread4 调用 notify(),最终,三个阻塞的线程只会有一个被唤醒,代码如下:
public class Main {
public static void main(String[] args) {
Object locker = new Object();
Thread thread1 = new Thread(() -> {
synchronized (locker) {
System.out.println("thread1调用wait(), 阻塞等待");
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("thread1被唤醒");
}
});
Thread thread2 = new Thread(() -> {
synchronized (locker) {
System.out.println("thread2调用wait(), 阻塞等待");
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("thread2被唤醒");
}
});
Thread thread3 = new Thread(() -> {
synchronized (locker) {
System.out.println("thread3调用wait(), 阻塞等待");
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("thread3被唤醒");
}
});
Thread thread4 = new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (locker) {
locker.notify();
System.out.println("thread4调用notify()");
}
});
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
执行结果:
如果将 notify() 换成 调用 notifyAll(),那么就会全部被唤醒,代码如下:
Thread thread4 = new Thread(() -> {
try {
Thread.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (locker) {
locker.notifyAll();
System.out.println("thread4调用notifyAll()");
}
}
通过结果可以看到,当 thread4 调用了 notifyAll() 后, thread3 先获取到了锁,然后释放锁后,thread2 又获取到了锁,最后是 thread1
💡wait() 和 sleep() 的区别
- 类不同:sleep() 是Thread线程类的静态方法, wait() 是 Object类的方法
- 调用后是否释放锁: sleep() 调用后不会有释放锁的操作; wait() 调用后会释放锁
- 用途不同: wait() 通常用于线程间交互/通信, sleep() 通常用于暂停执行
- 用法不同:wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用 notify() 方法, 或者 notifyAll() 方法 或者是使用wait(long timeout),指定一个阻塞时间,超时后线程自动苏醒。sleep() 方法执行完后,线程会自动苏醒。