概念和原理
操作系統中線程和進程的概念
線程是指進程中的一個執行流程,一個進程中可以運行多個線程。線程總是屬於某個進程,進程中的多個線程共享進程的內存。
Java中的線程
每個線程都有一個調用棧,即使不在程序中創建任何新的線程,線程也在後臺運行着。
一個Java應用總是從main()方法開始運行,main()方法運行在主線程內。
線程總體分兩類:用戶線程和守候線程。當所有用戶線程執行完畢時,JVM自動關閉。但是守候線程卻不獨立於JVM,守候線程一般是由操作系統或者用戶自己創建的。
創建和啓動
定義和實例化線程
- 繼承java.lang.Thread類,直接通過new來實例化。
- 實現java.lang.Runnable接口,用Thread的構造方法來實例化。
啓動線程
在線程的Thread對象上調用start()方法,之後發生了一系列複雜的事情:
- 啓動新的執行線程(具有新的調用棧);
- 該線程從新狀態轉移到可運行狀態;
- 當該線程獲得機會執行時,其目標run()方法將運行。
線程的調度是JVM的一部分,在一個CPU的機器上上,實際上一次只能運行一個線程。一次只有一個線程棧執行。JVM線程調度程序決定實際運行哪個處於可運行狀態的線程。
線程的狀態
- 新狀態
- 可運行狀態
- 運行狀態
- 等待/阻塞/睡眠狀態
- 死亡態
線程被阻止運行
不考慮IO阻塞的情況下,有如下3中情況:
- 睡眠;
- 等待;
- 因爲需要一個對象的鎖定而被阻塞。
睡眠Thread.sleep(long millis)
當線程睡眠時,它入睡在某個地方,在甦醒之前不會返回到可運行狀態。當睡眠時間到期,則返回到可運行狀態。
Thread.sleep()使當前線程睡眠至少多少毫秒(儘管它可能在指定的時間之前被中斷)。
讓步Thread.yield()
調用Thread.yield():不能保障太多事情,儘管通常它會讓當前運行線程回到可運行性狀態,使得有相同優先級的線程有機會執行。
yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。
線程總是存在優先級,優先級範圍在1~10之間。JVM線程調度程序是基於優先級的搶先調度機制。在大多數情況下,當前運行的線程優先級將大於或等於線程池中任何線程的優先級。但這僅僅是大多數情況。
join()
Thread的非靜態方法join()讓一個線程B“加入”到另外一個線程A的尾部。在A執行完畢之前,B不能工作。
然而,如果它加入的線程沒有存活,則當前線程不需要停止。
其他
還有下面幾種特殊情況可能使線程離開運行狀態:
1、線程的run()方法完成。
2、在對象上調用wait()方法(不是在線程上調用)。
3、線程不能在對象上獲得鎖定,它正試圖運行該對象的方法代碼。
4、線程調度程序可以決定將當前運行狀態移動到可運行狀態,以便讓另一個線程獲得運行機會,而不需要任何理由。
線程的同步與鎖
Java中每個對象都有一個內置鎖。
當程序運行到非靜態的synchronized同步方法上時,自動獲得與正在執行代碼類的當前實例(this實例)有關的鎖。獲得一個對象的鎖也稱爲獲取鎖、鎖定對象、在對象上鎖定或在對象上同步。
一個對象只有一個鎖。所以,如果一個線程獲得該鎖,就沒有其他線程可以獲得鎖,直到第一個線程釋放(或返回)鎖。這也意味着任何其他線程都不能進入該對象上的synchronized方法或代碼塊,直到該鎖被釋放。
當一個類已經很好的同步以保護它的數據時,這個類就稱爲“線程安全的”。
何時需要同步?
在多個線程同時訪問互斥(可交換)數據時,應該同步以保護數據,確保兩個線程不會同時修改更改它。
線程的交互
void notify()
喚醒在此對象監視器上等待的單個線程。
void notifyAll()
喚醒在此對象監視器上等待的所有線程。
void wait()
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法。
守護線程
public final void setDaemon(boolean on)將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。
該方法必須在啓動線程前調用。
參考網址:http://lavasoft.blog.51cto.com/62575/222742