進程: 一個正在執(zhí)行的程序.每個進程執(zhí)行都有一個執(zhí)行順序,該順序是一個執(zhí)行路徑,或叫一個控制單元. 一個進程至少有一個線程.
線程:就是進程中的一個獨立的控制單元. 線程控制這進程的執(zhí)行.
多進程的缺點:進程切換開銷大;進程間的通信很不方便。
多線程: 指的是在單個程序中可以同時運行多個不同的線程,執(zhí)行不同的任務,線程切換的開銷小 。
線程的生命周期
Java 做了很多工作,力求把這些細節(jié)抽象化。Java 提供了一個名為?Thread.State?的枚舉類型,囊括了操作系統(tǒng)看到的線程狀態(tài)。?Thread.State?中的值概述了一個線程的生命周期。

圖一
NEW
已經(jīng)創(chuàng)建線程,但還沒在線程對象上調用 start() 方法。所有線程一開始都處于這個狀態(tài)。
RUNNABLE
線程正在運行,或者當操作系統(tǒng)調度線程時可以運行。
Java實現(xiàn)內存管理和并發(fā)編程的方式。
BLOCKED阻塞狀態(tài)
線程中止運行,因為它在等待獲得一個鎖,以便進入聲明為 synchronized 的方法或代碼塊。
具備運行資格, 沒有執(zhí)行權。
WAITING
線程中止運行,因為它調用了 Object.wait() 或 Thread.join() 方法。
在sleep和wait時, 既沒有運行資格,有沒有執(zhí)行權。
TIMED_WAITING
線程中止運行,因為它調用了 Thread.sleep() 方法,或者調用了 Object.wait() 或Thread.join() 方法,而且傳入了超時時間。
TERMINATED
線程執(zhí)行完畢。線程對象的 run() 方法正常退出,或者拋出了異常。
可見性和可變性
在 Java 中,其實一個進程中的每個 Java 應用線程都有自己的棧(和局部變量),不過這些線程共用同一個堆,因此可以輕易在線程之間共享對象,畢竟需要做的只是把引用從一個線程傳到另一個線程。

我剛整理了一套2018最新的0基礎入門和進階教程,無私分享,加Java學習q-u-n :六七八,二四一,五六三 即可獲取,內附:開發(fā)工具和安裝包,以及系統(tǒng)學習路線圖
圖一
由此引出 Java 的一個一般設計原則——對象默認可見。如果我有一個對象的引用,就可以復制一個副本,然后將其交給另一個線程,不受任何限制。Java 中的引用其實就是類型指針,指向內存中的一個位置,而且所有線程都共用同一個地址空間,所以默認可見符合自然規(guī)律。
除了默認可見之外,Java 還有一個特性對理解并發(fā)很重要——對象是可變的(mutable),對象的內容(實例字段的值)一般都可以修改。使用 final 關鍵字可以把變量或引用聲明為常量,但這種字段不屬于對象的內容。
這兩個特性(跨線程可見性和對象可變性)結合在一起,大大增加了理解 Java 并發(fā)編程的難度。
并發(fā)編程的安全性
如果我們想編寫正確的多線程代碼,得讓程序滿足一個重要的條件,
即:在一個程序中,不管調用什么方法,也不管操作系統(tǒng)如何調度應用線程,一個對象看到的任何其他對象都不處于非法或不一致的狀態(tài),這樣的程序才稱得上是 安全的多線程程序 。
互斥(mutual exclusion)和狀態(tài)保護
只要修改或讀取對象的過程中,對象的狀態(tài)可能不一致,這段代碼就要受到保護。為了保護這種代碼,Java 平臺只提供了一種機制:互斥。
Java 為開發(fā)者提供了 synchronized 關鍵字。這個關鍵字可以用在代碼塊或方法上,使用時,Java 平臺會限制訪問代碼塊或方法中的代碼。
因為 synchronized 關鍵字把代碼包圍起來,所以很多開發(fā)者認為,Java 的
并發(fā)和代碼有關。有些資料甚至把 synchronized 修飾的塊或方法中的代碼
稱為 臨界區(qū) ,還認為臨界區(qū)是并發(fā)的關鍵所在。其實不然,稍后會看到,其
實我們要防范的是數(shù)據(jù)的不一致性。
Java 平臺會為它創(chuàng)建的每個對象記錄一個特殊的標記,這個標記叫監(jiān)視器(monitor)。synchronized 使用這些監(jiān)視器(或叫鎖)指明,隨后的代碼可以臨時把對象渲染成不一致的狀態(tài)。 synchronized 修飾的代碼塊或方法會發(fā)生一系列事件,詳述如下:
線程需要修改對象時,會臨時把對象變成不一致狀態(tài);
線程獲取監(jiān)視器,指明它需要臨時互斥存儲這個對象;
線程修改對象,修改完畢后對象處于一致的合法狀態(tài);
線程釋放監(jiān)視器。
同步是保護狀態(tài)的一種協(xié)助機制,因此非常脆弱。一個缺陷(需要使用
synchronized 修飾的方法卻沒有使用)就可能為系統(tǒng)的整體安全性帶來災難
性的后果。
之所以使用 synchronized 這個詞作為“需要臨時互斥存儲”的關鍵詞,除了說明需要獲取監(jiān)視器之外,還表明進入代碼塊時,JVM 會從主內存中重新讀取對象的當前狀態(tài)。類似地,退出 synchronized 修飾的代碼塊或方法時,JVM 會刷新所有修改過的對象,把新狀態(tài)存入主內存。
volatile關鍵字
Java 還提供了另一個關鍵字,用來并發(fā)訪問數(shù)據(jù)—— volatile 。這個關鍵字指明,應用代碼使用字段或變量前,必須重新從主內存讀取值。同樣,修改使用 volatile 修飾的值后,在寫入變量之后,必須存回主內存。
volatile 關鍵字的主要用途之一是在“關閉前一直運行”模式中使用。編寫多線程程序時,如果外部用戶或系統(tǒng)需要向處理中的線程發(fā)出信號,告訴線程在完成當前作業(yè)后優(yōu)雅關閉線程,那么就要使用 volatile 。這個過程有時叫作“優(yōu)雅結束”模式。
Thread 類中有用的方法
setName()和 getName()
開發(fā)者使用這兩個方法設定或取回單個線程的名稱。為線程起名字是個好習慣,因為這樣調試時更方便,尤其是使用 jvisualvm 等工具。
isAlive()
用來測試線程是否還“活著”。
start()
這個方法用來創(chuàng)建一個新應用線程,然后再調用 run() 方法調度這個線程,開始執(zhí)行。正常情況下,執(zhí)行到 run() 方法的末尾或者執(zhí)行 run() 方法中的一個 return 語句后,線程就會結束運行。
interrupt()
中斷線程. 如果調用 sleep() 、 wait() 或 join() 方法時阻塞了某個線程,那么在表示這個線程的Thread 對象上調用 interrupt() 方法,會讓這個線程拋出InterruptedException 異常(并把線程喚醒)。如果線程中涉及可中斷的 I/O 操作,那么這個 I/O 操作會終止,而且線程會收到 ClosedByInterruptException 異常。即便線程沒有從事任何可中斷的操作,線程的中斷狀態(tài)也會被設為 true。
join()
在調用 join() 方法的 Thread 對象“死亡”之前,當前線程一直處于等待狀態(tài)??梢园堰@個方法理解為一個指令,在其他線程結束之前,當前線程不會繼續(xù)向前運行。貌似只在start()后才生效.
setDaemon()
用戶線程是這樣一種線程,只要它還“活著”,進程就無法退出——這是線程的默認行為。有時,程序員希望線程不阻止進程退出——這種線程叫守護線程(可以理解為后臺線程)。一個線程是守護線程還是用戶線程,由 setDaemon() 方法控制。這個方法必須在invoked before the thread is started.
setUncaughtExceptionHandler()
線程因拋出異常而退出時,默認的行為是打印線程的名稱、異常的類型、異常消息和堆棧跟蹤。如果這么做還不夠,可以在線程中安裝一個自定義的處理程序,處理未捕獲的異常。
yield() 暫停當前正在執(zhí)行的線程對象,并執(zhí)行其他線程。
interrupt用法
如果一個線程處于了阻塞狀態(tài)(如線程調用了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中斷的通道上的 I/O 操作方法后可進入阻塞狀態(tài)),則在線程在檢查中斷標示時如果發(fā)現(xiàn)中斷標示為true,則會在這些阻塞方法(sleep、join、wait、1.5中的condition.await及可中斷的通道上的 I/O 操作方法)調用處拋出InterruptedException異常,并且在拋出異常后立即將線程的中斷標示位清除,即重新設置為false。拋出異常是為了線程從阻塞狀態(tài)醒過來,并在結束線程前讓程序員有足夠的時間來處理中斷請求。

圖三
Thread 類棄用的方法
Thread 類除了有一些有用的方法之外,還有一些危險的方法,開發(fā)者不應該使用。這些方法是 Java 線程 API 原來提供的,但很快就發(fā)現(xiàn)不適合開發(fā)者使用??上У氖?,因為 Java要向后兼容,所以不能把這些方法從 API 中移除。
stop()
如若不違背并發(fā)安全的要求,幾乎不可能正確使用 Thread.stop() ,因為 stop() 方法會立即“殺死”線程,不會給線程任何機會把對象恢復成合法狀態(tài)。這和并發(fā)安全等原則完全相悖,因此絕對不能使用 stop() 方法。
suspend() 、 resume() 和 countStackFrames()
調用 suspend() 方法掛起線程時,不會釋放這個線程擁有的任何一個監(jiān)視器,因此,如果其他線程試圖訪問這些監(jiān)視器,這些監(jiān)視器會變成死鎖。其實,這種機制會導致死鎖之間的條件競爭,而且 resume() 會導致這幾個方法不能使用。
destroy()
這個方法一直沒有實現(xiàn),如果實現(xiàn)了,會遇到與 suspend() 方法一樣的條件競爭。開發(fā)者始終應該避免使用這些棄用的方法。為了達到上述方法的預期作用,Java 開發(fā)了一些安全的替代模式。前面提到的“關閉前一直運行”模式就是這些模式的一例。