一题带你彻底理解 sleep() 和 wait()

简介:

一道Java的题目:

关于sleep()和wait(),以下描述错误的一项是:

  • A sleep是线程类(Thread)的方法,wait是Object类的方法;

  • B sleep不释放对象锁,wait放弃对象锁

  • C sleep暂停线程、但监控状态仍然保持,结束后会自动恢复

  • D wait后进入等待锁定池,只有针对此对象发出notify方法后获得对象锁进入运行状态

关于对象锁:

截取网上的一段话:

所有对象都自动含有单一的锁。
JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。 只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁。 每当任务离开一个synchronized(同步)方法,计数递减,当计数为0的时候,锁被完全释放,此时别的任务就可以使用此资源。

这段话令人感到迷惑,一个对象不是只有一个锁吗?只有获得这个对象的锁才能对它进行操作,若这个对象的锁被一个线程先获得,那就其他线程就需要等待。那多次加锁什么意思,锁不是依附于对象的吗?

在往下的文章中,我暂且理解为一个对象有且只有一把锁,锁在不同线程间传递,一个线程可以多次获得同一个对象的锁。暂且不考虑一个对象上多个锁这种方法是不是确实存在,这对下面影响不大。

关于锁池和等待池

在Java中,每个对象都有两个池,锁(monitor)池和等待池

  • 锁池 :假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。

  • 等待池 :假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。如果另外的一个线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.

深入理解:

如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。

优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

注:wait() ,notifyAll(),notify() 三个方法都是Object类中的方法.

关于wait() ,notifyAll(),notify() 三个方法

wait()

 
  1. public final void wait() throws InterruptedException,IllegalMonitorStateException

该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。在调用 wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用 wait()方法。进入 wait()方法后,当前线程释放锁。在从 wait()返回前,线程与其他线程竞争重新获得锁。如果调用 wait()时,没有持有适当的锁,则抛出 IllegalMonitorStateException,它是 RuntimeException 的一个子类,因此,不需要 try-catch 结构。

notify()

 
  1. public final native void notify() throws IllegalMonitorStateException

该方法也要在同步方法或同步块中调用,即在调用前,线程也必须要获得该对象的对象级别锁,的如果调用 notify()时没有持有适当的锁,也会抛出 IllegalMonitorStateException。

该方法用来通知那些可能等待该对象的对象锁的其他线程。如果有多个线程等待,则线程规划器任意挑选出其中一个 wait()状态的线程来发出通知,并使它等待获取该对象的对象锁(notify 后,当前线程不会马上释放该对象锁,wait 所在的线程并不能马上获取该对象锁,要等到程序退出 synchronized 代码块后,当前线程才会释放锁,wait所在的线程也才可以获取该对象锁),但不惊动其他同样在等待被该对象notify的线程们。当第一个获得了该对象锁的 wait 线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用 notify 语句,则即便该对象已经空闲,其他 wait 状态等待的线程由于没有得到该对象的通知,会继续阻塞在 wait 状态,直到这个对象发出一个 notify 或 notifyAll。这里需要注意:它们等待的是被 notify 或 notifyAll,而不是锁。这与下面的 notifyAll()方法执行后的情况不同。

notifyAll()

 
  1. public final native void notifyAll() throws IllegalMonitorStateException

该方法与 notify ()方法的工作方式相同,重要的一点差异是:

notifyAll 使所有原来在该对象上 wait 的线程统统退出 wait 的状态(即全部被唤醒,不再等待 notify 或 notifyAll,但由于此时还没有获取到该对象锁,因此还不能继续往下执行),变成等待获取该对象上的锁,一旦该对象锁被释放(notifyAll 线程退出调用了 notifyAll 的 synchronized 代码块的时候),他们就会去竞争。如果其中一个线程获得了该对象锁,它就会继续往下执行,在它退出 synchronized 代码块,释放锁后,其他的已经被唤醒的线程将会继续竞争获取该锁,一直进行下去,直到所有被唤醒的线程都执行完毕。

sleep()不会释放掉锁(监控)

最开始的那道题答案是D


原文发布时间为: 2018-11-16
本文作者: Java技术驿站
本文来自云栖社区合作伙伴“ Java技术驿站”,了解相关信息可以关注“ Java技术驿站”。

相关文章
|
1月前
|
Java
在多线程中sleep()和wait()的区别(详细)
在多线程中sleep()和wait()的区别(详细)
|
3月前
|
消息中间件 Java
剑指JUC原理-6.wait notify(下)
剑指JUC原理-6.wait notify
45 0
|
9月前
sleep () 和 wait () 的区别
sleep () 和 wait () 的区别
42 0
|
10月前
|
监控
Sleep()和wait()方法的区别
Sleep()和wait()方法的区别
74 0
|
10月前
|
Java 程序员
sleep 和 wait 的区别
Java 中,线程的 "sleep" 和 "wait" 方法区别
88 0
|
Java
java多线程中sleep和wait的4个区别,你知道几个?
sleep和wait的区别是面试中一个非常常见的问题,因为从表象来看,好像sleep和wait都能使线程处于阻塞状态,但是却有着本质上的却别。这篇文章就来好好分析一下。 整体的区别其实是有四个: 1、sleep是线程中的方法,但是wait是Object中的方法。 2、sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。 3、sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。 4、sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人中断)。 下面我们就根据这四个区别来分析。
256 0
java多线程中sleep和wait的4个区别,你知道几个?
线程 - wait & sleep 区别
线程 - wait & sleep 区别
90 0
wait()和sleep()的区别(巨详细)
wait()和sleep()的区别(巨详细)
395 0
wait()和sleep()的区别(巨详细)
|
Java
sleep与wait区别
第一个区别是在对系统资源的占用上。 wait是Object类的一个函数(也就意味着所有对象都有这个函数),指线程处于进入等待状态,此时线程不占用任何资源,不增加时间限制。wait可以被notify和notifyAll函数唤醒(当然这两个同时也是Object的函数)。 而sleep则是Thread类的一个函数,指线程被调用时,占着CPU不工作。此时,系统的CPU部分资源被占用,其他线程无法进入,会增加时间限制。
115 0