对象中的等待集(wait,notify,notifyAll)
- 我们知道线程是的操作是抢占式执行的,我们假想有多个线程在等待一个cpu给提供资源,但是其中线程x抢cpu抢的快,但是当前cpu并没有那个可以给x提供的资源,又因为这个x不停的抢cpu,导致其他线程不能进入到cpu中,因此会影响到程序的运行效率。
为了解决这个问题,我们引入了对象等待集,也就是wait,notify,notifyAll三个关键字,要和synchronized一起用
- wait 如果cpu中没有满足这个线程执行的资源时候,让这个线程等待,wait()会使线程状态发生变化(runnable–>waiting,waiting–>runnable)
- 等的是对像的等待集上(wait()是Object的一个方法,所以等在该对象的等待集上)
- 使用的时候必须加锁,等在哪个对象上,就对哪个对象加锁(wait()执行成功时会释放锁,醒来时会重新请求锁)
- notify 当cpu中的条件成熟的时候,通知指定的线程工作,唤醒一个线程
- notifyAll 是一个唤醒所有线程,让这些线程去竞争一把锁
- 哪个线程调用wait(),哪个线程进入等待集
举例分析
那么这种情况如何解决呢?
那就是该线程发现条件还没有成熟(也就是发现ATM机没钱的时候),就应该释放锁,并且等待,知道其他线程通知说条件已经成熟了,然后把该线程唤醒(也就是运钞扯来了,并且把钱放进ATM机了,或者是后面的人进去存钱,存好了之后跟他说我刚刚存钱了,你可以进去取钱了)
关键在于需要根据实际情况来手动控制线程之间的先后顺序。(虽然线程的执行顺序是由调度器实现的,但是有时候要控制先后顺序的hi后就需要手动实现)——这就引出了等待集
等待集的核心操作主要有两个
1.等待(wait方法)
2.通知(notify方法)
如
notify用法
import java.util.Scanner;
public class ThreadDemo12 {
private static Object object = new Object();
public static class MyThread extends Thread{
@Override
public void run() {
synchronized (object){
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 10; i < 20; i++) {
System.out.println(i);
}
}
}
public static void main(String[] args){
Thread thread = new MyThread();
thread.start();
Scanner scanner = new Scanner(System.in);
System.out.println("我不输入,Thread线程就不会运行");
scanner.nextInt();
synchronized (object){
object.notify();
}
}
}
注意
调用wait的前提是发现当前操作的条件不满足,要想知道条件满不满足,就需要先能够访问到资源(先获取到锁)
wait必须在同步代码块中才能使用,否则就会出现异常
wait方式做了三件事:
1.先释放锁(前提是已经获取到锁)
2.等待通知(等待其他线程调用notify方法,把它唤醒)
3.收到通知后会重新尝试获取锁
从内核角度分析
竞态条件问题:
wait:
1.释放锁
2.等待通知
3.获取锁
因为wait的前两步都是原子性操作,所有wait不存在竞态条件问题
wait 和 sleep 的对比
理论上:wait 和 sleep 是没有可比性的,wait()用于线程之间的通信的,sleep()使线程阻塞一段时间。
相同点:都可以让线程放弃执行一段时间。
- wait 之前需要请求锁,而wait执行时会先释放锁,等被唤醒时再重新请求锁。这个锁是 wait 对像上的 monitor
lock - sleep 是无视锁的存在的,即之前请求的锁不会释放,没有锁也不会请求。
- wait 是 Object 的方法
- sleep 是 Thread 的静态方法