天天看點

Thread之二:sleep、wait、yield、join

 《Thread之一:線程生命周期及五種狀态》

《Thread之二:sleep、wait、yield、join》

 《juc線程池原理(四): 線程池狀态介紹》

一、回顧下概念

Java中的多線程是一種搶占式的機制而不是分時機制。線程主要有以下幾種狀态:建立,就緒,運作,阻塞,死亡。搶占式機制指的是有多個線程處于就緒狀态,但是隻有一個線程在運作。

Thread之二:sleep、wait、yield、join

1.sleep()方法

  在指定時間内讓目前正在執行的線程暫停執行,但不會釋放“鎖标志”。不推薦使用。

  sleep()使目前線程進入阻塞狀态,在指定時間内不會執行。

2.wait()方法

  在其他線程調用對象的notify或notifyAll方法前,導緻目前線程等待。線程會釋放掉它所占有的“鎖标志”,進而使别的線程有機會搶占該鎖。

  目前線程必須擁有目前對象鎖。如果目前線程不是此鎖的擁有者,會抛出IllegalMonitorStateException異常。

  喚醒目前對象鎖的等待線程使用notify或notifyAll方法,也必須擁有相同的對象鎖,否則也會抛出IllegalMonitorStateException異常。

  waite() 和notify()必須在synchronized函數或synchronized block中進行調用。如果在non-synchronized函數或non-synchronized block中進行調用,雖然能編譯通過,但在運作時會發生 IllegalMonitorStateException的異常。

3.yield方法

  暫停目前正在執行的線程對象。

  yield()隻是使目前線程重新回到可執行狀态,是以執行yield()的線程有可能在進入到可執行狀态後馬上又被執行。

  yield()隻能使同優先級或更高優先級的線程有執行的機會。

4.join方法

  等待該線程終止。

  等待調用join方法的線程結束,再繼續執行。如:t.join();//主要用于等待t線程運作結束,若無此句,main則會執行完畢,導緻結果不可預測。

二、線程同步

一個線程結束的标志是:run()方法結束。

一個機鎖被釋放的标志是:synchronized塊或方法結束。

當有多個線程通路共享資料的時候,就需要對線程進行同步。線程同步相關的方法中的幾個主要方法的按照所屬可以分成:

  • Thread類的方法:sleep(),yield()等
  • Object的方法:wait()和notify()、notifyAll()等

Object中的對象頭存放的鎖資訊在控制同步通路時使用。見《java對象在記憶體中的結構(HotSpot虛拟機)》和《Synchronized之二:synchronized的實作原理》

Wait()方法和notify()方法:當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去了對象的機鎖。當它被一個notify()方法喚醒時,等待池中的線程就被放到了鎖池中。該線程從鎖池中獲得機鎖,然後回到wait()前的中斷現場。

Thread類中的方法:

由于sleep()方法是Thread類的方法,是以它不能改變對象的機鎖。是以當在一個Synchronized方法中調用sleep()時,線程雖然休眠了,但是對象的機鎖沒有被釋放,其他線程仍然無法通路這個對象。而wait()方法則會線上程休眠的同時釋放掉機鎖,其他線程可以通路該對象。

Yield()方法:是停止目前線程,讓同等優先權的線程運作。如果沒有同等優先權的線程,那麼Yield()方法将不會起作用。

join()方法:是由一個線程調用另一個線程,調用線程等待被調用線程終止。

sleep()與wait()的共同點及不同點:

共同點: 他們都是在多線程的環境下,都可以在程式的調用處阻塞指定的毫秒數,并傳回。

不同點: Thread.sleep(long)可以不在synchronized的塊下調用,而且使用Thread.sleep()不會丢失目前線程對任何對象的同步鎖(monitor);

              object.wait(long)必須在synchronized的塊下來使用,調用了之後失去對object的monitor, 這樣做的好處是它不影響其它的線程對object進行操作。

舉個java.util.Timer的例子來說明。

Thread之二:sleep、wait、yield、join
private void main Loop() {
        while (true) {
        ....
        synchronized(queue) {
        .....
        if (!taskFired) // Task hasn't yet fired; wait
               queue.wait(executionTime - currentTime);
        }
}      
Thread之二:sleep、wait、yield、join
private void sched(TimerTask task, long time, long period) {
          synchronized(queue) {
              ...
              queue.add(task);
          }
}      

繼續閱讀