这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战
前言
关于前面的章节也有介绍到关于线程的wait()和notify()以及notifyAll()等方法的使用,今天来简单介绍一下常见的面试问题。
常见问题
- 为什么 wait 必须在 synchronized 同步代码中使用?
- 为什么 wait/notify/notifyAll 被定义在 Object 类中,而 sleep 定义在 Thread 类中?
为什么 wait 必须在 synchronized 同步代码中使用?
简单来说就是,如果wait()方法不用synchronize 同步代码中使用,在准备调用wait()方法时,就有可能资源被抢夺,线程被CPU暂停了,导致调用wait()方法失败;
在前面介绍的生产者消费者章节中可以看到,下述为生产者消费者伪代码 ↓ ;
- 如果consume (消费者)调用list.wait() 方法时,没有加synchronize同步代码修饰,线程被CPU暂停,wait()失败
- 在product(生产者) 中调用notify() 方法时,就没有线程需要唤醒,导致死锁,双方线程一直在等待的情况。
- 如果我们对两个代码块都加锁的话,所有的执行就都是原子性的了,就不会出现消费者线程list.size()==0 和 list.wait()中间断开,导致生产者线程的notify()插入其中 的情况了。这样的话程序就安全了。
- 所以wait 为什么必须在 synchronized 同步代码中使用?就是为了程序的安全性,另外想要调用wait()方法释放锁,也必须先要持有锁对象才可以,否则的话是会报
IllegalMonitorStateException异常的。
class MyQueue {
private final ArrayList<String> list = new ArrayList<>(5);
public void consume() {
while(true) {
if(list.size()==0) {
list.wait();
}
System.out.println(list.remove(0));
list.notify();
}
}
public void product() {
while(true) {
if(list.size()==5) {
list.wait();
}
list.add("element");
list.notify();
}
}
}
为什么 wait/notify/notifyAll 被定义在 Object 类中,而 sleep 定义在 Thread 类中?
可能我们司空见惯了wait/notify/notifyAll 在Object类中,直接使用它,但是并不关心它为什么这么设置的原因。针对这个问题,其实也好理解,主要原因如下:
- 因为Java中每个对象都有一个锁(monitor或者监视器),Java的对象头是有锁的关联标识;而wait/notify/notifyAll的目的是为了等待其它线程释放持有的对象的锁,或者唤醒其它线程持有对象的锁,而且Java线程中也没有任何可供对象使用的锁和同步器,另外将这些方法定义在Object中,也是为了使得Java的每一个对象都可以用于线程间通信。
- 因为对象中的锁是对象级别的,而非线程级别的,wait/notify/notifyAll也都是针对锁级别的操作,它们的锁属于对象,所以把它们定义在Object类中是最合适,因为Object类是所有对象的父类。
- 如果把wait/notify/notifyAll 方法定义在 Thread 类中,我们该如何让一个线程持有多把锁?又如何确定线程等待的是哪一把锁?既然是线程等待某一个对象的锁,那么操作对象是最为合适的,而不是线程。