多線程是Java學習的很是重要的方面,是每一個Java程序員必須掌握的基本技能。本文只是多線程細節、本質的總結,並沒有代碼例子入門,不適合初學者理解。初學者學習多線程,建議一邊看書、看博文,以便寫代碼嘗試。html
轉載請註明出處——http://www.cnblogs.com/zrtqsk/p/3776328.html謝謝!java
1、進程程序員
進程是操做系統結構的基礎;是一次程序的執行;是一個程序及其數據在處理機上順序執行時所發生的活動。操做系統中,幾乎全部運行中的任務對應一條進程(Process)。一個程序進入內存運行,即變成一個進程。進程是處於運行過程當中的程序,而且具備必定獨立功能。描述進程的有一句話很是經典——進程是系統進行資源分配和調度的一個獨立單位。多線程
進程是系統中獨立存在的實體,擁有本身獨立的資源,擁有本身私有的地址空間。進程的實質,就是程序在多道程序系統中的一次執行過程,它是動態產生,動態消亡的,具備本身的生命週期和各類不一樣的狀態。進程具備併發性,它能夠同其餘進程一塊兒併發執行,按各自獨立的、不可預知的速度向前推動。 併發
(注意,併發性(concurrency)和並行性(parallel)是不一樣的。並行指的是同一時刻,多個指令在多臺處理器上同時運行。併發指的是同一時刻只能有一條指令執行,但多個進程指令被被快速輪換執行,看起來就好像多個指令同時執行同樣。)學習
進程由程序、數據和進程控制塊三部分組成。spa
2、線程操作系統
線程,有時被稱爲輕量級進程(Lightweight Process,LWP),是程序執行流的最小單元。一個標準的線程由線程ID,當前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程本身不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的所有資源。一個線程能夠建立和撤消另外一個線程,同一進程中的多個線程之間能夠併發執行。因爲線程之間的相互制約,導致線程在運行中呈現出間斷性。每個程序都至少有一個線程,若程序只有一個線程,那就是程序自己。線程
在Java Web中要注意,線程是JVM級別的,在不中止的狀況下,跟JVM共同消亡,就是說若是一個Web服務啓動了多個Web應用,某個Web應用啓動了某個線 程,若是關閉這個Web應用,線程並不會關閉,由於JVM還在運行,因此別忘了設置Web應用關閉時中止線程。指針
3、線程狀態
(圖片出處:http://www.cnblogs.com/skywang12345/p/3479024.html)
線程共包括如下5種狀態。
1. 新建狀態(New) : 線程對象被建立後,就進入了新建狀態。此時它和其餘Java對象同樣,僅僅由Java虛擬機分配了內存,並初始化其成員變量值。
2. 就緒狀態(Runnable): 也被稱爲「可執行狀態」。線程對象被調用了該對象的start()方法,該線程處於就緒狀態。Java虛擬機會爲其建立方法調用棧和程序計數器。處於就緒狀態的線程,隨時可能被CPU調度執行,取決於JVM中線程調度器的調度。
3. 運行狀態(Running) : 線程獲取CPU權限進行執行。須要注意的是,線程只能從就緒狀態進入到運行狀態。
4. 阻塞狀態(Blocked) : 阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。阻塞的狀況分三種:
(01) 等待阻塞 -- 經過調用線程的wait()方法,讓線程等待某工做的完成。
(02) 同步阻塞 -- 線程在獲取synchronized同步鎖失敗(由於鎖被其它線程所佔用),它會進入同步阻塞狀態。
(03) 其餘阻塞 -- 經過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入就緒狀態。
5. 死亡狀態(Dead) : 線程執行完了、因異常退出了run()方法或者直接調用該線程的stop()方法(容易致使死鎖,如今已經不推薦使用),該線程結束生命週期。
4、wait()、notify()、nofityAll()方法
在Object.java中,定義了wait(), notify()和notifyAll()等方法。
wait()的做用是讓當前線程進入等待狀態,同時,wait()也會讓當前線程釋放它所持有的鎖。
而 notify()和notifyAll()的做用,則是喚醒當前對象上的等待線程;notify()是喚醒單個線程,而notifyAll()是喚醒全部的線程。
Object類中關於等待/喚醒的API詳細信息以下:
notify() -- 喚醒在此對象監視器上等待的單個線程,使其進入「就緒狀態」。
notifyAll() -- 喚醒在此對象監視器上等待的全部線程,使其進入「就緒狀態」。
wait() -- 讓當前線程處於「等待(阻塞)狀態」,「直到其餘線程調用此對象的 notify() 方法或 notifyAll() 方法」,當前線程被喚醒(進入「就緒狀態」)。
wait(long timeout) -- 讓當前線程處於「等待(阻塞)狀態」,「直到其餘線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量」,當前線程被喚醒(進入「就緒狀態」)。
wait(long timeout, int nanos) -- 讓當前線程處於「等待(阻塞)狀態」,「直到其餘線程調用此對象的 notify() 方法或 notifyAll() 方法,或者其餘某個線程中斷當前線程,或者已超過某個實際時間量」,當前線程被喚醒(進入「就緒狀態」)。
wait()的做用是讓「當前線程」等待(會釋放鎖),而「當前線程」是指正在cpu上運行的線程!
此處,http://www.cnblogs.com/skywang12345/p/3479224.html例子講的很是詳細。
5、yield()、sleep()、join()和interrupt()方法
一、yield()
yield()是Thread類的靜態方法。它能讓當前線程暫停,但不會阻塞該線程,而是由「運行狀態」進入到「就緒狀態」,從而讓 其它具備相同優先級的等待線程獲取執行權;可是,並不能保證在當前線程調用yield()以後,其它具備相同優先級的線程就必定能得到執行權;也有多是 當前線程又進入到「運行狀態」繼續運行!
值得注意的是,yield()方法不會釋放鎖。
二、sleep()
sleep()是Thread類的靜態方法。該方法聲明拋出了InterrupedException異常。因此使用時,要麼捕捉,要麼聲明拋出。
有2種重載方式:
——static void sleep(long millis) : 讓當前正在執行的線程暫停millis毫秒,並進入阻塞狀態,該方法受到系統計時器和線程調度器的精度和準度的影響。
——static void sleep(long millis , int nanos) : 讓當前正在執行的線程暫停millis毫秒加nanos微秒,並進入阻塞狀態,該方法受到系統計時器和線程調度器的精度和準度的影響。
sleep() 的做用是讓當前線程休眠,即當前線程會從「運行狀態」進入到「休眠(阻塞)狀態」。sleep()會指定休眠時間,線程休眠的時間會大於/等於該休眠時間;在線程從新被喚醒時,它會由「阻塞狀態」變成「就緒狀態」,從而等待cpu的調度執行。經常使用來暫停程序的運行。
同時注意,sleep()方法不會釋放鎖。
三、join()
join() 是Thread的一個實例方法。表示,當某個程序執行流中調用其餘線程的join方法時,調用線程將被阻塞,直到被join的線程執行完畢。
有3種重載的形式:
——join() : 等待被join的線程執行完成
——join(long millis) : 等待被join的線程的時間最長爲millis毫秒,若在millis毫秒內,被join的線程還未執行結束,則不等待。
——join(long millis , int nanos) : 等待被join的線程的時間最長爲millis毫秒加nanos微秒,若在此時間內,被join的線程還未執行結束,則不等待。
即當前線程內,用某個線程對象調用join()後,會使當前線程等待,直到該線程對象的線程運行完畢,原線程纔會繼續運行。
四、interrupt()
咱們常常經過判斷線程的中斷標記來控制線程。
interrupt()是Thread類的一個實例方法,用於中斷本線程。這個方法被調用時,會當即將線程的中斷標誌設置爲「true」。因此當中斷處於「阻塞狀態」的線程時,因爲處於阻塞狀態,中斷標記會被設置爲「false」,拋出一個 InterruptedException。因此咱們在線程的循環外捕獲這個異常,就能夠退出線程了。
interrupt()並不會中斷處於「運行狀態」的線程,它會把線程的「中斷標記」設置爲true,因此咱們能夠不斷經過isInterrupted()來檢測中斷標記,從而在調用了interrupt()後終止線程,這也是一般咱們對interrupt()的用法。
Interrupted()是Thread類的一個靜態方法,它返回一個布爾類型指明當前線程是否已經被中斷,isInterrupted()是Thread類的實例方法,返回一個布爾類型來判斷線程是否已經被中斷。它們都可以用於檢測對象的「中斷標記」。區別是,interrupted()除了返回中斷標記以外,它還會清除中斷標記(即將中斷標記設爲false);而isInterrupted()僅僅返回中斷標記。
6、 Synchronized關鍵字
一、原理
在java中,每個對象有且僅有一個同步鎖。這也意味着,同步鎖是依賴於對象而存在。
噹噹前線程調用某對象的synchronized方法時,就獲取了該對象的同步鎖。例如,synchronized(obj),當前線程就獲取了「obj這個對象」的同步鎖。
不一樣線程對同步鎖的訪問是互斥的。也就是說,某時間點,對象的同步鎖只能被一個線程獲取到!經過同步鎖,咱們就能在多線程中,實現對「對象/方法」的互斥訪問。 例如,如今有個線程A和線程B,它們都會訪問「對象obj的同步鎖」。假設,在某一時刻,線程A獲取到「obj的同步鎖」並在執行一些操做;而此時,線程B也企圖獲取「obj的同步鎖」 —— 線程B會獲取失敗,它必須等待,直到線程A釋放了「該對象的同步鎖」以後線程B才能獲取到「obj的同步鎖」從而才能夠運行。
二、基本規則
第一條 : 當一個線程訪問「某對象」的「synchronized方法」或者「synchronized代碼塊」時,其餘線程對「該對象」的該「synchronized方法」或者「synchronized代碼塊」的訪問將被阻塞。
第二條 : 當一個線程訪問「某對象」的「synchronized方法」或者「synchronized代碼塊」時,其餘線程仍然能夠訪問「該對象」的非同步代碼塊。
第三條 : 當一個線程訪問「某對象」的「synchronized方法」或者「synchronized代碼塊」時,其餘線程對「該對象」的其餘的「synchronized方法」或者「synchronized代碼塊」的訪問將被阻塞。
三、實例鎖和全局鎖
實例鎖 -- 鎖在某一個實例對象上。若是該類是單例,那麼該鎖也具備全局鎖的概念。
實例鎖對應的就是synchronized關鍵字。
全局鎖 -- 該鎖針對的是類,不管實例多少個對象,那麼線程都共享該鎖。
全局鎖對應的就是static synchronized(或者是鎖在該類的class或者classloader對象上)。
就是說,一個非靜態方法上的synchronized關鍵字,表明該方法依賴其所屬對象。一個靜態方法上synchronized關鍵字,表明該方法依賴這個類自己。
7、線程優先級和守護線程
一、線程優先級
java中的線程優先級的範圍是1~10,默認的優先級是5。每一個線程默認的優先級都與建立它的父線程具備相同的優先級。默認狀況下,mian線程具備普通優先級。「高優先級線程」會優先於「低優先級線程」執行。Thread提供了setPriority(int newPriority)和getPriority()方法來設置和返回線程優先級。
Thread類有3個靜態常量:
——MAX_PRIORITY = 10
——MIN_PRIORITY = 1
——NORM_PRIORITY = 5
二、守護線程
java 中有兩種線程:用戶線程和守護線程。能夠經過isDaemon()方法來區別它們:若是返回false,則說明該線程是「用戶線程」;不然就是「守護線程」。
用戶線程通常用戶執行用戶級任務,而守護線程也就是「後臺線程」,通常用來執行後臺任務。須要注意的是:Java虛擬機在「用戶線程」都結束後會後退出。
守護線程又稱「後臺線程」、「精靈線程」,它有一個特徵——若是全部前臺線程都死亡,後臺線程自動死亡。
經過setDaemon(true)來設置一個線程。
關於Java各類知識的總結,推薦你們一位博主skywang12345, 他的各類Java知識總結實在是詳細易懂且經典,給了我很多幫助。
參考:http://www.cnblogs.com/skywang12345/p/java_threads_category.html
《瘋狂Java講義》
若是以爲本文還不錯的話,麻煩點擊推薦哦!謝謝啦!