JAVA學習筆記--線程狀態及對象鎖
https://my.oschina.net/goldenshaw/blog?catalog=3277710&temp=1499785479453
[TOC]
一、線程狀態
-
【NEW】 新建尚未運行
還沒調用start,或者調用了start()方法,不一定立即改變線程狀態,中間可能需要一些步驟才完成一個線程的啟動
-
【RUNNABLE】 處于可運行狀態:正在運行或準備運行
start調用結束,線程由NEW變成RUNNABLE,存活著并嘗試占用CPU資源,yield操作,線程還是RUNNABLE狀態,只是他有一個細節的內部變化,做一個簡單的讓步。在JAVA層面是RUNNABLE的狀態,并不代表一定處于運行中的狀態,比例BIO中,線程正阻塞在網絡等待的時候,看到的狀態依然是RUNNABLE狀態,而底層線程已經被阻塞了。
-
【BLOCKED】 等待獲取鎖時進入的狀態
線程被掛起了,通常是因為它在等待一個鎖,當synchronized正好有線程在使用時,另外一個線程嘗試進入這個臨界區就會被阻塞,直到另一個線程走完臨界區,或者發生響應鎖對象的wait操作,他才有機會去爭奪臨界區的權利。搶到鎖之后,狀態從阻塞轉到RUNNABLE
線程等待IO操作會進入BLOCKED狀態 -
【WAITING】 通過wait方法進入的等待
當wart/join/park方法調用后,無限期地等待另一個線程來執行某一特定操作的線程處于WAITING狀態。
-
【TIMED_WAITING】 等待另一個線程來執行取決于指定等待時間的操作的線程處于這種狀態。
通過wait(t),sleep(t),join(t),parkNanos,parkUntil等方法進入此狀態。當時間達到時觸發線程回到工作狀態Runnable。
interrupt只對處于waiting或timed_waiting狀態的線程起作用,對其他狀態不起作用。 -
【TERMINATED】 已退出的線程處于這種狀態。
線程結束了,就處于這種狀態,也就是run方法運行完了。在操作系統內部可能已經注銷了相應的線程,或者將它復用給其他需要使用線程的請求。
New->Runnable: start()
Runnable->Blocked: lock
Blocked-->Runnable: unLock
Runnable->Waiting: wait()/sleep()/join()/IO
Waiting-->Runnable: wait、sleep、join結束/IO結束
Runnable->Timed_waiting: wait(T)/sleep(T)/join(T)
Timed_waiting-->Runnable: wait、sleep、join時間到
Runnable->Terminated: 正常退出/異常
BLOCKED和WAITING狀態區別:
A、blocked是虛擬機認為程序還不能進入某個區域,因為同時進去就會有問題,這是一塊臨界區。
B、發生wait等操作的先決條件是要進入臨界區,也就是線程已經拿到鎖了,自己可能進去做了一些事情,但此時通過判定業務上的參數,發現還有一些其他配合的資源沒有準備充分,那么自己就等等再做其他事情。
在waiting狀態下,如果發生了interrupt操作,則處于該狀態的線程在內部會拋出一個InterruptedException【跑出異常后,線程還需要獲取到對應鎖才能繼續執行后續操作】,這個異常應當在run方法內捕獲,使得run方法正常地執行完成,當然捕獲異常后,是決定讓線程繼續運行,還是結束等要根據業務場景才處理。
IO操作會導致線程進入WAITING狀態 參考 https://my.oschina.net/goldenshaw/blog/705397
二、對象鎖
- 所有對象都自動含有單一的鎖
- JVM負責跟蹤對象被加鎖的次數。第一個線程第一次獲取到對象鎖時候,鎖計數為1,此線程再次獲取這個對象的鎖計數加一,釋放一次減一直到全部釋放(計數為0)其他線程才可以獲取此對象的鎖。
- 只有首先獲得鎖的任務(線程)才能繼續獲取該對象上的多個鎖。
Object.wait()
線程必須先獲取到對象監視鎖,才能調用此方法。執行此方法線程立即釋放對象監視鎖,當前線程阻塞。若方法拋出InterruptedException異常,同樣需要獲取鎖才能繼續往下執行
Object.notify()
線程必須先獲取到對象監視鎖,才能調用此方法。執行此方法會喚起wait的線程,當前線程不會立即是否鎖,直到臨界區代碼庫執行完線程釋放鎖之后,其他線程才有機會去競爭鎖。
Thread.sleep() //當前線程阻塞,不釋放鎖
Thread.yield() //讓出CPU執行時間,不釋放鎖