线程中sleep()、wait()方法,以及对象锁、锁池、等待池的理解
由一道关于sleep()和wait()方法的题目展开
关于sleep()和wait(),以下描述错误的一项是:
- A sleep是线程类(Thread)的方法,wait是Object类的方法;
- B sleep不释放对象锁,wait放弃对象锁
- C sleep暂停线程、但监控状态仍然保持,结束后会自动恢复
- D wait后进入等待锁定池,只有针对此对象发出notify方法后获得对象锁进入运行状态
关于A
sleep是Thread类中的方法,而wait、notify、notifyAll都是Object类中的方法。
sleep、wait的使用范围:
sleep是Thread类中的静态方法。因此无论是在a线程中调用b的sleep方法,还是在b线程中调用a的sleep方法,谁调用谁就sleep。也因此,sleep可以在任何地方使用。
wait、notify、notifyAll就很惨了,只能在同步控制方法或同步控制块中使用。
关于B、C
经过一个英语渣渣copy部分源代码并且翻译之后得到的内容是
sleep():
使当前执行线程休眠(暂时停止执行),以指定毫秒数加上指定的纳秒数,以系统定时器和调度器的精度和准确性为准。线程不会丢失任何监视器(monitor)的所有权。
- 因为sleep()并没释放锁,所以仍旧处在同步状态,监控仍旧存在,睡眠时间结束后自动恢复运行。
wait():
当前线程必须拥有该对象的监视器(monitor)。线程释放此监视器(monitor)的所有权,并等待,直到另一个线程通过调用{@code notify}方法或{@code notifyAll}方法通知等待此对象的监视器的线程醒来。然后线程等待,直到它能够重新获得监视器的所有权并恢复执行。
- wait()释放掉锁,所以不再处于同步状态。
注:对象锁就是常说的同步锁——synchronized。监视器(monitor)或者说其所有权我也理解为对象锁,毕竟锁住以后就又拥有的权利了,以上翻译如若有错,百度的锅。
关于D
这里就需要提及两个概念,Java中每个对象都有两个池:锁池、等待池。
锁池:假设线程A已经拥有了某个对象(不是类)的锁,而其他线程也想要调用这个对象的某个synchronized方法或者代码块。由于这些线程在进入对象的synchronized方法或者代码块时,必须要先获得该对象的锁的拥有权,但是该对象的锁正在被线程A拥有,所以这些线程就进入了该对象的锁池。
等待池:假设线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(原因是wait()必须出现在synchronized锁里面,自然执行wait()方法前就拥有了这个对象的锁,源代码翻译中提到wait()释放监视器所有权,即释放锁),同时线程A进入该对象的等待池中。如果另一个线程调用了该相同对象的notifyAll()方法,那么处于该对象中的所有线程会进入该对象的锁池中,准备争夺锁的拥有权。如果另一个线程调用了该相同对象的notify()方法,那么仅仅只有一个处于该对象的等待池中的线程(随机的某个)进入锁池,准备得到锁的拥有权。
简单理解:
如果线程调用了对象的wait()方法,那么线程就处于该对象的等待池中,等待池中的线程不会去争夺锁的拥有权。
当线程调用了该对象的notify()方法或者notifyAll()方法,被唤醒的线程进入锁池,准备争夺锁的拥有权。假如某个线程没有争夺到锁,它仍旧停留在锁池中等待下一次的争夺,只有再次调用wait()方法才会进入等待池中。
以上线程拿到锁要干嘛?进入就绪状态,等待CPU时间片开始运行。
完整代码演示:
public class MultiThread {
private static class Thread1 implements Runnable{
@Override
public void run() {
//由于 Thread1和下面Thread2内部run方法要用同一对象作为监视器,如果用this则Thread1和Threa2的this不是同一对象
//所以用MultiThread.class这个字节码对象,当前虚拟机里引用这个变量时指向的都是同一个对象
synchronized(MultiThread.class){
System.out.println("thread1 创建...");
System.out.println("thread1 就绪中");
try{
//释放锁有两种方式:(1)程序自然离开监视器的范围,即离开synchronized关键字管辖的代码范围
//(2)在synchronized关键字管辖的代码内部调用监视器对象的wait()方法。这里使用wait方法
MultiThread.class.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("thread1 正在运行 ...");
System.out.println("thread1 结束!");
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run() {
//notify方法并不释放锁,即使thread2调用了下面的sleep方法休息10ms,但thread1仍然不会执行
//因为thread2没有释放锁,所以Thread1得不到锁而无法执行
synchronized(MultiThread.class){
System.out.println("thread2 创建...");
System.out.println("thread2 此刻唤醒其他wait线程notify other thread can release wait status ...");
MultiThread.class.notify();
try{
System.out.println("thread2 睡了...");
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("thread2 正在运行...");
System.out.println("thread2 结束!");
}
}
}
public void demo() {
for(int i = 0;i<5;i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("第"+i+"次输出");
}
}
public static void main(String[] args) {
new Thread(new Thread1()).start();
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
new Thread(new Thread2()).start();
new MultiThread().demo();
}
}