java之Thread.sleep(long)與object.wait()/object.wait(long)的區別及相關概念梳理(good)


 

一、Thread.sleep(long)與object.wait()/object.wait(long)的區別
sleep(long)與wait()/wait(long)行為上有些類似,主要區別如下:
1.Thread.sleep(long)是屬於Thread類的靜態方法。其基本語義是使當前運行的線程暫停一段時間。實現細節是把當前線程放入就緒線程隊列中,直到睡眠時間到期才可被調度為執行線程(在時間到期前無法被調度為執行線程)。此方法可以在sychronized代碼塊中,調用此方法不釋放對象鎖;也可以不在sychronized代碼塊中。此方法的作用線程一定是當前運行的線程(如果代碼在一個線程類中,不一定是代碼所在的線程實例),即使是在線程對象上調用sleep(long)或調用Thread.currentThread().sleep(long)。


2.object.wait()是屬於類實例(Object及其子類實列,也包括Class類實例)的方法。
其基本語義是使當前線程等待,直到被通知,默認是this.wait()。實現細節是把當前線程放入阻塞線程隊列中,並把當前線程注冊為指定對象的監聽器,並鎖釋放指定對象的鎖;當被notify/notifyAll通知時,重新爭取指定對象的鎖,並把當前線程從指定對象的監聽器中移除,把當前線程從阻塞隊列放入就緒隊列,等待被調度。
此方法必須在sychronized代碼塊中,且鎖定的對象要與等待通知來源的對象一致。而wait(long)方法阻塞時放入的是就緒隊列,等待時間到期或被通知就可被調度,其他與wait()方法相同。
如果當前線程不是對象所得持有者,該方法拋出一個java.lang.IllegalMonitorStateException 異常”
http://blog.csdn.net/intlgj/article/details/6245226

注:從上可基本認識到線程的執行、就緒、阻塞三種狀態的切換,以及線程的調度(操作系統調度線程)和就緒線程隊列、阻塞線程隊列(實際實現可能更復雜,比如優先級,調度策略等)。可認識到object(包括Class類的實例)的wait/notify/notifyAll的對象監聽和通知事件模式,以及對象上的鎖、鎖隊列(實際實現可能更復雜,比如優先級,鎖爭用等)、阻塞線程監聽隊列(notify時只通知一個監聽器,具體調度未知,另有自動喚醒,條件喚醒)。

http://blog.csdn.net/qingmingcom/article/details/6590967

 

    wait()方法與notify()必須要與synchronized(resource)一起使用。也就是wait與notify針對已經獲取了resource鎖的線程進行操作
從語法角度來說就是Obj.wait(),Obj.notify必須在synchronized(Obj){...}語句塊內。
從功能上來說wait()線程在獲取對象鎖后,主動釋放CPU控制權,主動釋放對象鎖,同時本線程休眠。直到有其它線程調用對象的notify()喚醒該線程,才能繼續獲取對象鎖,並繼續執行。
相應的notify()就是對對象鎖的釋放操作
【因此,我們可以發現,
wait和notify方法均可釋放對象的鎖,但wait同時釋放CPU控制權,即它后面的代碼停止執行,線程進入阻塞狀態,而notify方法不立刻釋放CPU控制權,而是在相應的synchronized(){}語句塊執行結束,再自動釋放鎖
釋放鎖后,JVM會在等待resoure的線程中選取一線程,賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了在線程間同步、喚醒的操作。
Thread.sleep()與Object.wait()二者都可以暫停當前線程,釋放CPU控制權,主要的區別在於Object.wait()在釋放CPU同時,釋放了對象鎖的控制
而在同步塊中的
Thread.sleep()方法並不釋放鎖,僅釋放CPU控制權。

  1)wait()、notify()和notifyAll()方法是本地方法,並且為final方法,無法被重寫。

  2)調用某個對象的wait()方法能讓當前線程阻塞,並且當前線程必須擁有此對象的monitor(即鎖,或者叫管程)

  3)調用某個對象的notify()方法能夠喚醒一個正在等待這個對象的monitor的線程,如果有多個線程都在等待這個對象的monitor,則只能喚醒其中一個線程;

  4)調用notifyAll()方法能夠喚醒所有正在等待這個對象的monitor的線程;

     在Java中,是沒有類似於PV操作、進程互斥等相關的方法的。JAVA的進程同步是通過synchronized()來實現的,需要說明的是,Java的synchronized()方法類似於操作系統概念中的互斥內存塊,在Java中的Object類對象中,都是帶有一個內存鎖的,在有線程獲取該內存鎖后,其它線程無法訪問該內存,從而實現Java中簡單的同步、互斥操作。明白這個原理,就能理解為什么synchronized(this)與synchronized(static XXX)的區別了,synchronized就是針對內存區塊申請內存鎖,this關鍵字代表類的一個對象,所以其內存鎖是針對相同對象的互斥操作,而static成員屬於類專有,其內存空間為該類所有成員共有,這就導致synchronized()對static成員加鎖,相當於對類加鎖,也就是在該類的所有成員間實現互斥,在同一時間只有一個線程可訪問該類的實例。如果需要在線程間相互喚醒就需要借助Object類的wait()方法及nofity()方法。

http://blog.csdn.net/lingzhm/article/details/44940823

什么是monitor?

在HotSpot虛擬機中,monitor采用ObjectMonitor實現。

每個線程都有兩個ObjectMonitor對象列表,分別為free和used列表,如果當前free列表為空,線程將向全局global list請求分配ObjectMonitor。

ObjectMonitor對象中有兩個隊列:
_WaitSet 和 _EntryList,用來保存ObjectWaiter對象列表;
_owner指向獲得ObjectMonitor對象的線程。

_WaitSet :處於wait狀態的線程,會被加入到wait set;
_EntryList:處於等待鎖block狀態的線程,會被加入到entry set;

ObjectWaiter

ObjectWaiter對象是雙向鏈表結構,保存了_thread(當前線程)以及當前的狀態TState等數據, 每個等待鎖的線程都會被封裝成ObjectWaiter對象。

http://www.jianshu.com/p/f4454164c017

 

我剛開始深入研究多線程,一直認為Object.wait()/Condition.await()讓當前線程阻塞的同時,也會釋放當前線程對該condition對象的鎖。在之前的一些測試代碼中也顯示wait后,線程上的鎖被釋放了。
查看Object.wait()API 描述如下:

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0). 
    The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method.
 The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

其中“the thread releases ownership of this monitor” 說道當前線程會釋放這個對象監控器的所有權。

問題一:這里的monitor怎么理解?監視器(monitor)和鎖是什么關系? 這個monitor就是一個術語,或者是一個特殊的類(只包含私有域),但是在java中並沒有嚴格遵守這個概念。個人認為可以簡單的理解為鎖。

同樣的,Condition.await()方法中也有類似描述。
The lock associated with this Condition is atomically released and the current thread becomes disabled for thread scheduling purposes...
也說道,調用await()有,這個條件對象關聯的鎖被“原子級地”釋放。。。

問題二:這個原子級的釋放是什么意思? “原子級”其實就是為了保證一個操作的完整性,原子級的釋放保證了一個原子操作不會因為線程的突然掛起或者說阻塞而破壞這次操作。

這都能說明調用wait()或者await()后,當前線程會釋放該條件對象關聯的鎖吧?


兩個線程用object1做 wait/notify, 是這樣:
thread1 得到object1 的 monitor, 調用 object1.wait()
- 釋放object1 的 monitor, thread1 wait;

thread2 得到 object1 的 monitor, 調用 object1.notify()
- 激活thread1, 釋放object1 的 monitor;

thread1 得到 object1 的 monitor, 從object1.wait()返回, thread1接着執行.
關於monitor, 這個是多進程/線程同步的 一個術語, 見:
Operating Systems Design and Implementation, Third Edition
section 2.2

A monitor is a collection of procedures, variables, and data structures that are all grouped together in a special kind of module or package. 
Processes may call the procedures in a monitor whenever they want to, but they cannot directly access the monitor's internal data structures from procedures declared outside the monitor. 
This rule, which is common in modern object-oriented languages such as Java, was relatively unusual for its time, although objects can be traced back to Simula 67.

In all cases, before this method can return the current thread must
re-acquire the lock associated with this condition. When the
thread returns it is guaranteed to hold this lock.

會釋放,其他線程執行Condition.signal(),之前的線程會重新獲得鎖,繼續執行,
AbstractQueuedSynchronizer.java 第2040行,釋放鎖

https://segmentfault.com/q/1010000002390706

 


免責聲明!

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



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