現代操做系統在運行一個程序時,會爲其建立一個進程。例如,啓動一個Java程序,操做系統就會建立一個Java進程。現代操做系統調度的最小單元是線程,也叫輕量級進程(Light Weight Process),在一個進程裏能夠建立多個線程,這些線程都擁有各自的計數器、堆棧和局部變量等屬性,而且可以訪問共享的內存變量。處理器在這些線程上高速切換,讓使用者感受到這些線程在同時執行。html
Java線程經過調用線程的start()方法進行啓動,隨着run()方法的執行完畢,線程也隨之終止。java
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
複製代碼
Java線程在運行的生命週期中可能處於6種不一樣的狀態,在給定的一個時刻,線程只能處於其中的一個狀態。以下內容截取JDK 1.8 Thread.java的源碼:編程
Monitor是 Java中用以實現線程之間的互斥與協做的主要手段,它能夠當作是對象的鎖。每個對象都有,也僅有一個 monitor。bash
在HotSpot JVM中,monitor是由ObjectMonitor實現的,其主要數據結構以下(位於HotSpot虛擬機源碼ObjectMonitor.hpp文件,C++實現的):數據結構
ObjectMonitor() {
_header = NULL;
_count = 0; //記錄個數
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL; //處於wait狀態的線程,會被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //處於等block狀態的線程,會被加入到該列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
複製代碼
ObjectMonitor中主要有如下4個參數:併發
當多個線程同時訪問一段同步代碼時,首先會進入 _EntryList 集合,當線程獲取到對象的monitor 後進入 _Owner 區域並把monitor中的owner變量設置爲當前線程。同時monitor中的計數器count加1,若線程調用 wait() 方法,將釋放當前持有的monitor,owner變量恢復爲null,count自減1,同時該線程進入 _WaitSet集合中等待被喚醒。若當前線程執行完畢也將釋放monitor(鎖)並復位變量的值,以便其餘線程進入獲取monitor(鎖)。以下圖所示:post
Deadlock:死鎖線程,通常指多個線程調用間,進入相互資源佔用,致使一直等待沒法釋放的狀況。ui
Runnable:通常指該線程正在執行狀態中,該線程佔用了資源 。spa
Waiting on condition:等待資源,或等待某個條件的發生 。操作系統
Blocked:線程阻塞,是指當前線程執行過程當中,所須要的資源長時間等待卻一直未能獲取到,被容器的線程管理器標識爲阻塞狀態,能夠理解爲等待資源超時的線程。
Waiting for monitor entry :在線程嘗試執行同步代碼前,在monitor的」Entry Set「隊列中的等待線程。
In Object.wait(): 當線程得到了 Monitor,進入了臨界區以後,若是發現線程繼續運行的條件沒有知足,它則調用對象(通常就是被 synchronized 的對象)的 wait() 方法,放棄了 Monitor,進入 「Wait Set」隊列。
下圖描述了對象、對象的監視器、同步隊列和執行線程之間的關係:
從圖中能夠看到
等待/通知機制,是指一個線程A調用了對象O的wait()方法進入等待狀態,而另外一個線程B調用了對象O的notify()或者notifyAll()方法,線程A收到通知後從對象O的wait()方法返回,進而執行後續操做。上述兩個線程經過對象O來完成交互,而對象上的wait()和notify/notifyAll()的關係就如同開關信號同樣,用來完成等待方和通知方之間的交互工做。
等待/通知的相關方法是任意Java對象都具有的,由於這些方法被定義在全部對象的超類java.lang.Object上,方法和描述以下:
notify(): 通知一個在對象上等待的線程,使其從wait()方法返回,而返回的前提是該線程獲取到了對象的鎖。
notifyAll(): 通知全部等待在該對象上的線程。
wait(): 調用該方法的線程進入WAITING狀態,只有等待另外線程的通知或被中斷纔會返回,須要注意,調用wait()方法後,會釋放對象的鎖。
wait(long): 超時等待一段時間,這裏單位是毫秒。
wait(long, int): 超時狀態更細粒度的控制,能夠達到納秒。
注意:
從上述能夠發現,等待/通知機制依託於同步機制,其目的就是確保等待線程從wait()方法返回時可以感知到通知線程對變量作出的修改。
下面是上述示例的過程圖:
WaitThread首先獲取了對象的鎖,而後調用對象的wait()方法,從而放棄了鎖並進入了對象的等待隊列WaitQueue中,進入Waiting狀態。
因爲WaitThread釋放了對象的鎖,NotifyThread隨後獲取了對象的鎖,並調用對象的notify()方法,將WaitThread從WaitQueue移到SynchronizedQueue中,此時WaitThread的狀態變爲Blocked狀態。
NotifyThread釋放了鎖以後,WaitThread再次獲取到鎖並從wait()方法返回繼續執行。
等待/通知的經典範式分爲兩個部分:等待方和通知方。 等待方遵循以下原則:
synchronized(對象) {
while(條件不知足) {
對象.wait();
}
對應的處理邏輯
}
複製代碼
通知方遵循以下原則:
synchronized(對象) {
改變條件
對象.notifyAll();
}
複製代碼
這部份內容能夠參見深刻理解Java併發編程之經過JDK C++源碼以及Debug源碼死扣Thread.join()
這部份內容能夠參見深刻理解Java併發編程之把ThreadLocal扣爛