本博客系列是學習併發編程過程當中的記錄總結。因爲文章比較多,寫的時間也比較散,因此我整理了個目錄貼(傳送門),方便查閱。html
併發編程系列博客傳送門java
多線程是Java編程中一塊很是重要的內容,其中涉及到不少概念。這些概念咱們平時常常掛在嘴上,可是真的要讓你介紹下這些概念,你可能還真的講不清楚。這篇博客就總結下多線程編程中常常用到的概念,理解這些概念能幫助咱們更好地掌握多線程編程。linux
進程和線程是最常提到的概念了。在linux中,線程與進程最大的區別就是是否共享同一塊地址空間,並且共享同一塊地址空間的那一組線程將顯現相同的PID號。下面介紹下二者的概念:程序員
談到線程和進程,又勢必會涉及到線程號和進程號的概念。下面列舉了各個ID的概念。面試
下面貼上一張圖來解釋下這兩個概念:算法
上圖中的咖啡就能夠當作是CPU,上面的只有一個咖啡機,至關於只有一個CPU。想喝咖啡的人只有等前面的人制做完咖啡才能製做本身的開發,也就是同一時間只能有一我的在製做咖啡,這是一種併發模式。下面的圖中有兩個咖啡機,至關於有兩個CPU,同一時刻能夠有兩我的同時製做咖啡,是一種並行模式。數據庫
咱們發現並行編程中,很重要的一個特色是系統具備多核CPU。要是系統是單核的,也就談不上什麼並行編程了。編程
這個概念多是在多線程編程中說起最多的一個概念了。在面試過程當中,我試着問過幾個面試者,可是幾乎沒人能將這個概念解釋的很好的。安全
關於這個概念,我以爲好多人都有一個誤區,包括我本身一開始也是這樣的。我一開始認爲線程安全講的是某個共享變量線程安全,其實咱們所說的線程安全是指某段代碼或者是某個方法是線程安全的。線程安全的準肯定義應該是這樣的:微信
若是線程的隨機調度順序不影響某段代碼的最後執行結果,那麼咱們認爲這段代碼是線程安全的。
爲了保證代碼的線程安全,Java中推出了不少好用的工具類或者關鍵字,好比volatile、synchronized、ThreadLocal、鎖、併發集合、線程池和CAS機制等。這些工具並非在每一個場景下都能知足咱們多線程編程的需求,並非在每一個場景下都有很高的效率,須要咱們程序員根據具體的場景來選擇最適合的技術,這也許就是咱們程序員存在的價值所在。(我一直以爲若是有一個技術能很好的解決大多數場景下的問題,那麼這個領域確定是能夠作成機器自動化的。那麼對於這個領域就不太須要有多少人蔘與了。)
線程1佔用了鎖A,等待鎖B,線程2佔用了鎖B,等待鎖A,這種狀況下就形成了死鎖。在死鎖狀態下,相關的代碼將不能再提供服務。
private void deadLock() { Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (lock1) { try { Thread.currentThread().sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("1"); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (lock2) { synchronized (lock1) { System.out.println("2"); } } } }); t1.start(); t2.start(); }
這段代碼只是演示死鎖的場景,在現實中你可能不會寫出這樣的代碼。可是,在一些更爲複雜的場景中,你可能會遇到這樣的問題,好比t1拿到鎖以後,由於一些異常狀況沒有釋放鎖(死循環)。又或者是t1拿到一個數據庫鎖,釋放鎖的時候拋出了異常,沒釋放掉。
若是你懷疑代碼中有線程出現了死鎖,你能夠dump線程,而後查看線程狀態有沒有Blocked的線程(java.lang.Thread.State: BLOCKED)
"Thread-2" prio=5 tid=7fc0458d1000 nid=0x116c1c000 waiting for monitor entry [116c1b000] java.lang.Thread.State: BLOCKED (on object monitor) at com.ifeve.book.forkjoin.DeadLockDemo$2.run(DeadLockDemo.java:42) - waiting to lock <7fb2f3ec0> (a java.lang.String) - locked <7fb2f3ef8> (a java.lang.String) at java.lang.Thread.run(Thread.java:695) "Thread-1" prio=5 tid=7fc0430f6800 nid=0x116b19000 waiting for monitor entry [116b18000] java.lang.Thread.State: BLOCKED (on object monitor) at com.ifeve.book.forkjoin.DeadLockDemo$1.run(DeadLockDemo.java:31) - waiting to lock <7fb2f3ef8> (a java.lang.String) - locked <7fb2f3ec0> (a java.lang.String) at java.lang.Thread.run(Thread.java:695)
避免死鎖的幾個方式:
飢餓是指某一個或者多個線程由於種種緣由沒法得到所須要的資源,致使一直沒法執行。好比它的線程優先級可能過低,而高優先級的線程不斷搶佔它須要的資源,致使低優先級線程沒法工做。
在天然界中,母鳥給雛鳥餵食時很容易出現這種狀況:因爲雛鳥不少,食物有限,雛鳥之間的食物競爭可能很是厲害,常常搶不到食物的雛鳥有可能會被餓死。線程的飢餓很是相似這種狀況。
此外,某一個線程一直佔着關鍵資源不放,致使其餘須要這個資源的線程沒法正常執行,這種狀況也是飢餓的一種。與死鎖相比,飢餓仍是有可能在將來一段時間內解決的(好比,高優先級的線程已經完成任務,再也不瘋狂執行)。
活鎖是一種很是有趣的狀況。不知道你們是否遇到過這麼一種場景,當你要坐電梯下樓時,電梯到了,門開了,這時你正準備出去。但很不巧的是,門外一我的擋着你的去路,他想進來。因而,你很禮貌地靠左走,避讓對方。同時,對方也很是禮貌地靠右走,但願避讓你。結果,大家倆就又撞上了。因而乎,大家都意識到了問題,但願儘快避讓對方,你當即向右邊走,同時,他當即向左邊走。結果,又撞上了!不過介於人類的智能,我相信這個動做重複兩三次後,你應該能夠順利解決這個問題。由於這個時候,你們都會本能地對視,進行交流,保證這種狀況再也不發生。
但若是這種狀況發生在兩個線程之間可能就不會那麼幸運了。若是線程的智力不夠,且都秉承着「謙讓」的原則,主動將資源釋放給他人使用,那麼就會致使資源不斷地在兩個線程間跳動,而沒有一個線程能夠同時拿到全部資源正常執行。這種狀況就是活鎖。
這邊討論的同步和異步指的是同步方法和異步方法。
同步方法是指調用這個方法後,調用方必須等到這個方法執行完成以後才能繼續往下執行。
異步方法是指調用這個方法後會立馬返回,調用方能立馬往下繼續執行。被調用的異步方法實際上是由另外的線程進行執行的,若是這個異步方法有返回值的話能夠經過某種通知的方式告知調用方。
實現異步方法的方式:
涉及讀寫共享資源的代碼片斷叫「臨界區」。
好比下面代碼中,1處和2處就是一個代碼臨界區。
private static class BankAccount{ String accountName; double balance; public BankAccount(String accountName,double balance){ this.accountName = accountName; this.balance = balance; } public synchronized double deposit(double amount){ balance = balance + amount; //1 return balance; } public synchronized double withdraw(double amount){ balance = balance - amount; //2 return balance; } }
線程在CPU上運行以前須要CPU給這個線程分配時間片,當時間片運行完以後這個線程就會讓出CPU資源給其餘的線程運行。可是線程在將CPU資源讓出以前會保存當前的任務狀態以便下次得到CPU資源以後能夠繼續往下執行。因此線程從保存當前執行狀態到再加載的過程稱爲一次上下文切換。
減小上下文切換的措施
使用併發編程的目的是讓程序運行的更快(更大限度的使用CPU資源,讓程序運行更快),可是在進行併發編程的過程也會遇到一些挑戰。
PS:多線程併發編程可讓咱們最大限度的使用系統的CPU資源,以達到讓程序運行更快的目的(不是全部狀況下多線程都更快)。可是一個硬幣具備兩面性,引入多線程編程會給咱們帶來其餘的問題,好比說線程的上下文切換問題、共享變量的線程安全問題、線程間通訊問題、線程死鎖問題和硬件資源對多線程的影響等問題。其實研究多線程併發編程就是在研究這對矛盾體,怎麼在享受多線程併發編程給咱們帶來便利的同時又能避開多線程帶來的坑。JDK中給咱們提供不少多線程相關的類