15.wait和notify
线程的调度是无序的、随机的,抢占式执行的,但在实际的生活中,我们在某些情况下是需要有序执行的。那对于这种情况又该如何去解决呢?
wait方法:发现条件不满足或者说时机不成熟的时候就要阻塞等待。(让某个线程暂停下来等一等)
notify方法:其他线程构造了一个成熟的条件,可以唤醒等待。(把该线程唤醒,继续执行)
就比如说车辆行驶是无序的,随机的,有的车超速、抢占式的要往前跑,每个车都是一个独立的线程,但是当遇到红灯了,那么此时走在最前面的车辆就得停下等待,相应的后面的车也会跟着阻塞等待。只有当绿灯亮起,第一辆车被唤醒了,那么就可以通过了,紧接着就是后面的车辆陆陆续续的跟上过红绿灯。在这个过程中,第一辆车来讲也就是刚开始是wait()阻塞等待的,当绿灯亮起的时候,notify()让它唤醒,继续开始行驶。
注意:wait和notify都是Object对象的方法。只要是个类对象,都可以使用wait和notify。
wait方法的使用必须搭配synchronized使用,放在synchronized代码块里面,不然就会抛出以下异常
这个异常指的是非法锁状态异常,也就是说你还没获取到锁,就尝试去解锁,所以在这里可以清楚的知道在使用wait()方法的时候,必须要配合synchronized使用,因为wait()方法要做的事就包括解锁、阻塞等待、唤醒。
在添加了synchronized后,我们发现没有抛出异常了,但是这个线程确实一直在等待中,根据实际需要一直等待肯定是不行的,所以我们需要去唤醒这个线程,让他继续工作,在这里就使用的是notify()方法去唤醒。
package notebook;
public class ThreadTest15 {
public static void main(String[] args) throws InterruptedException{
Object object = new Object();
Thread thread1 = new Thread(() -> {
try {
System.out.println("wait 开始");
synchronized (object) {
System.out.println("等待中");
object.wait();
}
System.out.println("wait 结束");
}catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
Thread.sleep(2000);
Thread thread2 = new Thread(() -> {
synchronized (object) {
System.out.println("notify 开始");
System.out.println("唤醒");
object.notify();
System.out.println("notify 结束");
}
});
thread2.start();
}
}
在这段代码中,主线程启动了thread1和thread2两个线程,在线程1中进行wait()阻塞,在释放锁后,thread2执行notify()方法去唤醒,将thread1从wait()方法中唤醒出来,继续往下执行。同时在这段代码中,因为线程之间的通信是通过共享object这个对象实现的,所以wait()方法会释放当前线程的锁,进入到等待状态,知道线程2调用了notify()方法唤醒,让其继续执行。
16.notifyAll()方法
notify()方法是唤醒某一个线程等待的,使用notifyAll()方法可以一次唤醒所有的等待线程。比如下面的例子,在上面代码中进行更改。
package notebook;
public class ThreadTest15 {
public static void main(String[] args) throws InterruptedException{
Object object = new Object();
Thread thread1 = new Thread(() -> {
try {
System.out.println("wait 开始");
synchronized (object) {
System.out.println("等待中");
object.wait();
}
System.out.println("wait 结束");
}catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
Thread thread3 = new Thread(() -> {
try {
System.out.println("wait 开始2");
synchronized (object) {
System.out.println("等待中2");
object.wait();
}
System.out.println("wait 结束2");
}catch (InterruptedException e) {
e.printStackTrace();
}
});
thread3.start();
Thread thread4 = new Thread(() -> {
try {
System.out.println("wait 开始3");
synchronized (object) {
System.out.println("等待中3");
object.wait();
}
System.out.println("wait 结束3");
}catch (InterruptedException e) {
e.printStackTrace();
}
});
thread4.start();
// 唤醒线程
Thread.sleep(3000);
Thread thread2 = new Thread(() -> {
synchronized (object) {
System.out.println("notifyAll 开始");
System.out.println("唤醒所有等待线程");
object.notifyAll();
System.out.println("notifyAll 结束");
}
});
thread2.start();
}
}
17.wait()和sleep()的对比(重要)
wait()方法有一个带参数的版本是用来体现超时时间的,相对于sleep()来将sleep()它是体现的是当前线程休眠的时间,这两个一个是用于线程之间的通信的,一个是让线程阻塞等待一段时间。它们共同点也就是都让线程停止执行一段时间。但我们要清楚的知道这两个是要解决什么问题。wait()是要解决的是线程之间的顺序控制,sleep()只是单纯的让当前线程休眠一会。并且wait()需要搭配synchronized使用,sleep()不需要,wait()是Object的方法,sleep()是Thread的静态方法。
以上就是关于线程阻塞等待和唤醒的一些记录,可能存在很多不足,请见谅啦。