/**
* Causes the current thread to wait until either another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object, or a
* specified amount of time has elapsed.
① 導(dǎo)致當(dāng)前線程處于等待狀態(tài)晴竞,直到另外的線程調(diào)用Notify方法或者NotifyAll方法或 者指定的時間已經(jīng)過
* <p>
* The current thread must own this object's monitor.
* <p>
* This method causes the current thread (call it <var>T</var>) to
* place itself in the wait set for this object and then to relinquish
* any and all synchronization claims on this object. Thread <var>T</var>
* becomes disabled for thread scheduling purposes and lies dormant
* until one of four things happens:
該方法會導(dǎo)致當(dāng)前線程進(jìn)入該對象的等待集合里面并且會放棄該對象的所有同步聲明。
此時的線程處于休眠狀態(tài)并且對于線程的調(diào)度是不可用狀態(tài)庵寞,直到下面四件事中有一種發(fā)生。
* <ul>
* <li>Some other thread invokes the {@code notify} method for this
* object and thread <var>T</var> happens to be arbitrarily chosen as
* the thread to be awakened.
1、其他線程調(diào)用對象的notify方法配并,并且該線程正好成為任意被選中喚醒的線程
* <li>Some other thread invokes the {@code notifyAll} method for this
* object.
2丑搔、其他線程調(diào)用這個對象的NotifyAll方法
* <li>Some other thread {@linkplain Thread#interrupt() interrupts}
* thread <var>T</var>.
3厦瓢、其他線程出現(xiàn)了中斷
* <li>The specified amount of real time has elapsed, more or less. If
* {@code timeout} is zero, however, then real time is not taken into
* consideration and the thread simply waits until notified.
4、指定的等待時間已經(jīng)到期啤月。如果指定時間為0煮仇,則線程不會考慮這個時間并且一直處于等待直到被喚醒。
* </ul>
* The thread <var>T</var> is then removed from the wait set for this
* object and re-enabled for thread scheduling. It then competes in the
* usual manner with other threads for the right to synchronize on the
* object; once it has gained control of the object, all its
* synchronization claims on the object are restored to the status quo
* ante - that is, to the situation as of the time that the {@code wait}
* method was invoked. Thread <var>T</var> then returns from the
* invocation of the {@code wait} method. Thus, on return from the
* {@code wait} method, the synchronization state of the object and of
* thread {@code T} is exactly as it was when the {@code wait} method
* was invoked.
//然后將線程從該對象的等待集中刪除谎仲,并重新啟用線程調(diào)度浙垫。該線程使用通常的方式與其他線程競爭獲取該對象的鎖,一旦獲得了對象的控制權(quán),它對對象的所有同步聲明都將恢復(fù)到原樣-即夹姥,恢復(fù)到調(diào)用wait方法時的情況杉武。然后,線程從調(diào)用wait方法返回佃声。 因此艺智,從wait方法返回時,對象和線程的同步狀態(tài)與調(diào)用wait方法時的狀態(tài)完全相同圾亏。
* <p>
* A thread can also wake up without being notified, interrupted, or
* timing out, a so-called <i>spurious wakeup</i>. While this will rarely
* occur in practice, applications must guard against it by testing for
* the condition that should have caused the thread to be awakened, and
* continuing to wait if the condition is not satisfied. In other words,
* waits should always occur in loops, like this one:
//線程也可以喚醒十拣,不需要被通知,中斷或超時志鹃,即所謂的虛假喚醒夭问。 雖然這很少
在實踐中發(fā)生時,應(yīng)用程序必須通過測試應(yīng)該導(dǎo)致線程喚醒的條件來防止出現(xiàn)這種
情況曹铃,并且如果條件不滿足則繼續(xù)等待
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait(timeout);
* ... // Perform action appropriate to condition
* }
* </pre>
* (For more information on this topic, see Section 3.2.3 in Doug Lea's
* "Concurrent Programming in Java (Second Edition)" (Addison-Wesley,
* 2000), or Item 50 in Joshua Bloch's "Effective Java Programming
* Language Guide" (Addison-Wesley, 2001).
*
* <p>If the current thread is {@linkplain java.lang.Thread#interrupt()
* interrupted} by any thread before or while it is waiting, then an
* {@code InterruptedException} is thrown. This exception is not
* thrown until the lock status of this object has been restored as
* described above.
*
* <p>
* Note that the {@code wait} method, as it places the current thread
* into the wait set for this object, unlocks only this object; any
* other objects on which the current thread may be synchronized remain
* locked while the thread waits.
②注意缰趋,當(dāng)當(dāng)前線程處于等待的狀態(tài)時,wait方法僅僅將當(dāng)前線程放到了這個對象的等待集合里面陕见,僅僅解鎖了這個對象秘血,其他任何在當(dāng)前線程被同步的對象始終保持加鎖的狀態(tài)
* <p>
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
③此方法只能由作為該對象的監(jiān)視器的所有者的線程調(diào)用。 有關(guān)線程可以成為監(jiān)
視器所有者的方式的描述评甜,請參見notify方法灰粮。
*
* @param timeout the maximum time to wait in milliseconds.
* @throws IllegalArgumentException if the value of timeout is
* negative.
* @throws IllegalMonitorStateException if the current thread is not
* the owner of the object's monitor.
* @throws InterruptedException if any thread interrupted the
* current thread before or while the current thread
* was waiting for a notification. The <i>interrupted
* status</i> of the current thread is cleared when
* this exception is thrown.
* @see java.lang.Object#notify()
* @see java.lang.Object#notifyAll()
*/
public final native void wait(long timeout) throws InterruptedException;
①-1、代碼演示使用Object的Notify喚醒wait的線程
public class WaitWithNotify {
public static void main(String[] args) {
Object lock=new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+"獲得鎖");
try {
System.out.println(Thread.currentThread().getName()+"調(diào)用Wait");
lock.wait();
System.out.println(Thread.currentThread().getName()+"重新獲得鎖");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"線程1").start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+"獲得鎖");
System.out.println(Thread.currentThread().getName()+"調(diào)用Notify");
lock.notify();
}
}
},"Thread2").start();
}
}
線程1獲得鎖
線程1調(diào)用Wait
Thread2獲得鎖
Thread2調(diào)用Notify
線程1重新獲得鎖
①-2忍坷、代碼演示使用定時等待喚醒wait的線程
public class WaitWithTime {
public static void main(String[] args) {
Object lock=new Object();
new Thread(() -> {
synchronized (lock){
try {
System.out.println(Thread.currentThread().getName()+"獲得鎖");
System.out.println(Thread.currentThread().getName()+"即將進(jìn)入等待"+"粘舟,當(dāng)前時間:"+System.currentTimeMillis());
lock.wait(10000);
System.out.println(Thread.currentThread().getName()+"重新獲取鎖"+",當(dāng)前時間:"+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"線程1").start();
}
}
線程1獲得鎖
線程1即將進(jìn)入等待佩研,當(dāng)前時間:1619454972368
線程1重新獲取鎖柑肴,當(dāng)前時間:1619454982370
TIMED_WAITING
②代碼演示(wait方法僅僅將當(dāng)前線程放到了這個對象的等待集合里面,僅僅解鎖了這個對象旬薯,其他任何在當(dāng)前線程被同步的對象始終保持加鎖的狀態(tài))
public class WaitWithTwoLockResourse {
static Object lockA=new Object();
static Object lockB=new Object();
static class MyRunnable implements Runnable{
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"獲取到鎖A");
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"獲取到鎖B");
try {
System.out.println(Thread.currentThread().getName()+"即將調(diào)用Wait方法釋放鎖A資源");
lockA.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
static class LockBResource implements Runnable{
@Override
public void run() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"獲取到鎖B");
}
}
}
private static class MyTask3 implements Runnable{
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"獲取到鎖A");
}
}
}
public static void main(String[] args) {
Thread thread1=new Thread(new MyRunnable(),"Thread1");
thread1.start();
Thread thread2=new Thread(new LockBResource(),"Thread2");
thread2.start();
Thread thread3=new Thread(new MyTask3(),"Thread3");
thread3.start();
}
}
Thread1獲取到鎖A
Thread1獲取到鎖B
Thread1即將調(diào)用Wait方法釋放鎖A資源
Thread3獲取到鎖A
D:\android\progect\IdeaDemo>jstack 6964
2021-04-28 01:20:37
Full thread dump Java HotSpot(TM) Client VM (25.202-b08 mixed mode, sharing):
"DestroyJavaVM" #12 prio=5 os_prio=0 tid=0x0294dc00 nid=0x2254 waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE
"Thread2" #10 prio=5 os_prio=0 tid=0x15902c00 nid=0x2438 waiting for monitor entry [0x15cef000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.idea.thread.wait.WaitWithTwoLockResourse$LockBResource.run(WaitWithTwoLockResourse.java:35)
- waiting to lock <0x04afa6d8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Thread1" #9 prio=5 os_prio=0 tid=0x158fe000 nid=0x17c8 in Object.wait() [0x15c5f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x04afa6d0> (a java.lang.Object)
at java.lang.Object.wait(Object.java:502)
at com.idea.thread.wait.WaitWithTwoLockResourse$MyRunnable.run(WaitWithTwoLockResourse.java:16)
- locked <0x04afa6d8> (a java.lang.Object)
- locked <0x04afa6d0> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
jstack具體展示了某個jvm進(jìn)程在某時刻的堆椢铮快照。我們可以看到線程Thread1依次獲取了兩個鎖绊序,分別是0x04afa6d8(lockB)和0x04afa6d0(lockA)些侍。隨后線程Thread1進(jìn)入了waiting(on object monitor)狀態(tài),等待的鎖對象是0x04afa6d0(lockA)政模,對應(yīng)代碼里的lockA.wait()。
然后看線程Thread2蚂会,處于blocked(on object monitor)狀態(tài)淋样,等待的鎖對象是0x04afa6d8(lockB)⌒沧。可以證明線程Thread1在wait后并沒有釋放掉所有的鎖趁猴,只是釋放了代碼里調(diào)用wait()的鎖刊咳。
③-1:沒有該對象的鎖,調(diào)用wait方法將會拋出IllegalMonitorStateException
public class WaitBasic {
static Object lock=new Object();
public static void main(String[] args) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.idea.thread.wait.WaitBasic.main(WaitBasic.java:8)
③-2:獲取該對象的鎖,然后調(diào)用wait方法,此時主線程的狀態(tài)為WAITING(on object monitor)
public class WaitBasic {
static Object lock = new Object();
public static void main(String[] args) {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
"main" #1 prio=5 os_prio=0 tid=0x0268dc00 nid=0x2b2c in Object.wait() [0x0256f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x04af9760> (a java.lang.Object)
at java.lang.Object.wait(Object.java:502)
at com.idea.thread.wait.WaitBasic.main(WaitBasic.java:9)
- locked <0x04af9760> (a java.lang.Object)
原理
Entry Set And Wait Set
①等待獲取鎖
②獲取到鎖
⑥釋放鎖并退出
③線程獲取到了鎖儡司,但是調(diào)用了wait娱挨,就會釋放鎖,進(jìn)入等待集
④notify被喚醒后捕犬,進(jìn)入Set集合跷坝,去競爭獲取到鎖
⑤再次獲取到鎖