線程核心方法-wait

 /**
     * 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 (&lt;condition does not hold&gt;)
     *             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集合跷坝,去競爭獲取到鎖
⑤再次獲取到鎖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碉碉,隨后出現(xiàn)的幾起案子柴钻,更是在濱河造成了極大的恐慌,老刑警劉巖垢粮,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贴届,死亡現(xiàn)場離奇詭異,居然都是意外死亡蜡吧,警方通過查閱死者的電腦和手機(jī)毫蚓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昔善,“玉大人元潘,你說我怎么就攤上這事∫唬” “怎么了柬批?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長袖订。 經(jīng)常有香客問我氮帐,道長,這世上最難降的妖魔是什么洛姑? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任上沐,我火速辦了婚禮,結(jié)果婚禮上楞艾,老公的妹妹穿的比我還像新娘参咙。我一直安慰自己,他們只是感情好硫眯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布蕴侧。 她就那樣靜靜地躺著,像睡著了一般两入。 火紅的嫁衣襯著肌膚如雪净宵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音择葡,去河邊找鬼紧武。 笑死,一個胖子當(dāng)著我的面吹牛敏储,可吹牛的內(nèi)容都是我干的阻星。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼已添,長吁一口氣:“原來是場噩夢啊……” “哼妥箕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起酝碳,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矾踱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后疏哗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呛讲,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年返奉,在試婚紗的時候發(fā)現(xiàn)自己被綠了贝搁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡芽偏,死狀恐怖雷逆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情污尉,我是刑警寧澤膀哲,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站被碗,受9級特大地震影響某宪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锐朴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一兴喂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧焚志,春花似錦衣迷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至膳沽,卻和暖如春佃迄,著一層夾襖步出監(jiān)牢的瞬間泼差,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工呵俏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滔灶。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓普碎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親录平。 傳聞我的和親對象是個殘疾皇子麻车,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容