线程中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();
	}
 
}

猜你喜欢

转载自blog.csdn.net/weixin_42621338/article/details/82899060