多線程中sleep和wait的區別,以及多線程的實現方式及原因,定時器--Timer


1.  Java中sleep和wait的區別

① 這兩個方法來自不同的類分別是,sleep來自Thread類,和wait來自Object類。

sleep是Thread的靜態類方法,誰調用的誰去睡覺,即使在a線程里調用b的sleep方法,實際上還是a去睡覺,要讓b線程睡覺要在b的代碼中調用sleep。

 

② 鎖: 最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。

sleep不出讓系統資源;wait是進入線程等待池等待,出讓系統資源,其他線程可以占用CPU。一般wait不會加時間限制,因為如果wait線程的運行資源不夠,再出來也沒用,要等待其他線程調用notify/notifyAll喚醒等待池中的所有線程,才會進入就緒隊列等待OS分配系統資源。sleep(milliseconds)可以用時間指定使它自動喚醒過來,如果時間不到只能調用interrupt()強行打斷。

Thread.sleep(0)的作用是“觸發操作系統立刻重新進行一次CPU競爭”。

③ 使用范圍:wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用。

   synchronized(x){  
      x.notify() 
     //或者wait() 
   }

2.可能在面試的時候面試官會問你java實現多線程的方式有哪些,你可能會知道java要提供繼承Thread類和實現runnable接口這兩種方式來實現線程,但是如果問為什么這樣設計呢?

 

首先說一下,我們知道java的集成只能是單繼承,不能多集成,這樣的話就會有缺陷,比如想增加一個功能的時候必須要去修改基類。而實現runnable接口的這種方式可以很好的解決java不能多繼承導致的缺陷。這是第一個原因。

再說第二個,我們知道實現runnable接口的方式代碼的寫法是這樣的:new Thread(new runnable(){ public void run(){    ....}})。在這種情況下我們可以看到其實整個的runnable對象中的代碼可以被多個Thread對象實例所使用共享,這樣就可以解決一個多個線程處理同一資源的情況。做到了線程安全。在這里我覺着有必要通過一個代碼的方式來解釋一下第二個優點是如何實現的。

 

3.首先來說一下Timer是怎么工作的,Timer 是按照一定的時間段或者一個時間點根據定時的定時任務進行執行的。

Timer這個java提供的定時器有如下的特點:

① 他是一個單線程的,也就是你啟動一個Timer定時器就是啟動了一個線程。

② Timer定時器默認的情況下不是守護線程,但是可以通過構造參數設置為守護線程,守護線程在沒有其他線程的情況下自己會掛掉。

③ 使用Timer定時器的時候 要跟一個TimerTask定時任務結合來使用。而且TimerTask其實底層就是一個隊列,在TimerTask中增加的任務會在定時器這個線程里面挨個的執行。TimerTask也有自己的cannel取消等方法。

④ TimerTask中的run方法無法拋出,所以要進行try catch捕獲,如果其中任何一個任務發生異常沒有被捕獲,則其他任務也將被終止。

我們看到TImer定時器這個類有兩個schedule方法。其中都有的就是一個TimerTask這個任務。我對這兩個方法進行了一個總結:

方法詳解:
(1)schedule(TimerTask task, Date executeTime)
當executeTime<=currentTime時,task任務會在currentTimer立即執行
當executeTime>currentTime時,task會在未來的executeTime執行
 
(2)schedule(TimerTask task, Date firstTime, long period)
當firstTime <=currentTime時,task任務會在currentTimer立即執行,
當firstTime >currentTime時,task會在未來的executeTime執行,
執行任務所用的時間taskUsedTime<peroid,則下一個任務執行的時間是上次任務執行完成的時間+peroid,任務按時間間隔peroid周期性執行任務
執行任務所用的時間taskUsedTime>peroid,則下一個任務執行的時間是上次任務執行完成的時間+taskUsedTime,任務按時間間隔taskUsedTime 周期性執行任務
 
 
(3)schedule(TimerTask task, long delay)
任務延遲delay毫秒進行執行
 
(4)schedule(TimerTask task, long delay, long period)
A、延遲delay毫秒第一次執行,
B、執行任務所用的時間taskUsedTime<peroid,則下一個任務執行的時間是上次任務執行完成的時間+peroid, 任務按時間間隔peroid周期性執行任務
C、執行任務所用的時間taskUsedTime>peroid,則下一個任務執行的時間是上次任務執行完成的時間+taskUsedTime, 任務按時間間隔taskUsedTime 周期性執行任務
 
(5)scheduleAtFixedRate(TimerTask task, long delay, long period)
 
(6)scheduleAtFixedRate(TimerTask task, Date firstTime,  long period)
         startTime = currentTime
A、當firstTime>currentTime,任務則在currentTime執行
B、當firstTime<currentTime,任務會發生追趕執行,追趕執行的次數expectCount=(currentTime-firstTime)/peroid+1;
  第一個peroid屬於追趕階段,如果追趕上則等待執行startTime+peroid時間任務,如果沒有追趕上則直接執行startTime+peroid時間的任務
 
 首先我們看一下Timer這個類的構造函數,因為我們知道我們再使用Timer這個類的時候我們只是創建了一個Timer對象,並沒有像Thread那樣主動的去調用start方法。所以我想答應也應該明白我們定時器的啟動是在構造函數中做的,沒錯,從源碼中我們可以得道驗證:

Timer是一個單獨的線程,從第152行我們就可以看到,我們可以設置線程的名稱,可以設置是否是守護線程,然后調用start方法定時器就起作用了。但是並不會立即執行。線程調用start后也不會立即執行,這在上一篇中已經有說到了,他其實是把當前的線程示例放到了一個線程組中等待被執行。

哪我們就看他是如何調度的也就是schdule是如何執行的呢。

 

 這里我們補充一下,queue是一個TaskQueue


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM