前言:Java語言一個重要的特色就是內置了對併發的支持,讓Java大受企業和程序員的歡迎。同時,若是想要提高本身的技術,Java併發知識必不可少,這裏簡單整理了一些相關內容,但願能夠起到拋磚引玉的做用。程序員
1,Java內存模型是什麼?算法
Java內存模型規定和指引Java程序在不一樣的內存架構、CPU和操做系統間有肯定性地行爲。它在多線程的狀況下尤爲重要。Java內存模型對一個線程所作的變更能被其它線程可見提供了保證,它們之間是先行發生關係。這個關係定義了一些規則讓程序員在併發編程時思路更清晰。好比,先行發生關係確保了:編程
線程內的代碼可以按前後順序執行,這被稱爲程序次序規則。緩存
對於同一個鎖,一個解鎖操做必定要發生在時間上後發生的另外一個鎖定操做以前,也叫作管程鎖定規則。安全
前一個對volatile的寫操做在後一個volatile的讀操做以前,也叫volatile變量規則。數據結構
一個線程內的任何操做必需在這個線程的start()調用以後,也叫做線程啓動規則。多線程
一個線程的全部操做都會在線程終止以前,線程終止規則。架構
一個對象的終結操做必需在這個對象構造完成以後,也叫對象終結規則。併發
可傳遞性框架
2,Java中interrupted 和isInterruptedd方法的區別?
interrupted() 和 isInterrupted()的主要區別是前者會將中斷狀態清除然後者不會。Java多線程的中斷機制是用內部標識來實現的,調用Thread.interrupt()來中斷一個線程就會設置中斷標識爲true。當中斷線程調用靜態方法Thread.interrupted()來檢查中斷狀態時,中斷狀態會被清零。
非靜態方法isInterrupted()用來查詢其它線程的中斷狀態且不會改變中斷狀態標識。簡單的說就是任何拋出InterruptedException異常的方法都會將中斷狀態清零。不管如何,一個線程的中斷狀態都有可能被其它線程調用中斷來改變。
3,Java中的同步集合與併發集合有什麼區別?
同步集合與併發集合都爲多線程和併發提供了合適的線程安全的集合,不過併發集合的可擴展性更高。在Java1.5以前程序員們只有同步集合來用且在多線程併發的時候會致使爭用,阻礙了系統的擴展性。Java5介紹了併發集合像ConcurrentHashMap,不只提供線程安全還用鎖分離和內部分區等現代技術提升了可擴展性。
無論是同步集合仍是併發集合他們都支持線程安全,他們之間主要的區別體如今性能和可擴展性,還有他們如何實現的線程安全上。
同步HashMap, Hashtable, HashSet, Vector, ArrayList 相比他們併發的實現(ConcurrentHashMap, CopyOnWriteArrayList, CopyOnWriteHashSet)會慢得多。形成如此慢的主要緣由是鎖, 同步集合會把整個Map或List鎖起來,而併發集合不會。併發集合實現線程安全是經過使用先進的和成熟的技術像鎖剝離。
好比ConcurrentHashMap 會把整個Map 劃分紅幾個片斷,只對相關的幾個片斷上鎖,同時容許多線程訪問其餘未上鎖的片斷。
一樣的,CopyOnWriteArrayList 容許多個線程以非同步的方式讀,當有線程寫的時候它會將整個List複製一個副本給它。
若是在讀多寫少這種對併發集合有利的條件下使用併發集合,這會比使用同步集合更具備可伸縮性。
4,什麼是線程池? 爲何要使用它?
建立線程要花費昂貴的資源和時間,若是任務來了才建立線程那麼響應時間會變長,並且一個進程能建立的線程數有限。爲了不這些問題,在程序啓動的時候就建立若干線程來響應處理,它們被稱爲線程池,裏面的線程叫工做線程。從JDK1.5開始,Java API提供了Executor框架讓你能夠建立不一樣的線程池。好比單線程池,每次處理一個任務;數目固定的線程池或者是緩存線程池(一個適合不少生存期短的任務的程序的可擴展線程池)
線程池的做用,就是在調用線程的時候初始化必定數量的線程,有線程過來的時候,先檢測初始化的線程還有空的沒有,沒有就再看當前運行中的線程數是否是已經達到了最大數,若是沒有,就新分配一個線程去處理。
就像餐館中吃飯同樣,從裏面叫一個服務員出來;但若是已經達到了最大數,就至關於服務員已經用盡了,那沒得辦法,另外的線程就只有等了,直到有新的「服務員」爲止。
線程池的優勢就是能夠管理線程,有一個高度中樞,這樣程序纔不會亂,保證系統不會由於大量的併發而由於資源不足掛掉。
5,Java中活鎖和死鎖有什麼區別?
活鎖:一個線程一般會有會響應其餘線程的活動。若是其餘線程也會響應另外一個線程的活動,那麼就有可能發生活鎖。同死鎖同樣,發生活鎖的線程沒法繼續執行。然而線程並無阻塞——他們在忙於響應對方沒法恢復工做。這就至關於兩個在走廊相遇的人:甲向他本身的左邊靠想讓乙過去,而乙向他的右邊靠想讓甲過去。可見他們阻塞了對方。甲向他的右邊靠,而乙向他的左邊靠,他們仍是阻塞了對方。
死鎖:兩個或更多線程阻塞着等待其它處於死鎖狀態的線程所持有的鎖。死鎖一般發生在多個線程同時但以不一樣的順序請求同一組鎖的時候,死鎖會讓你的程序掛起沒法完成任務。
6,如何避免死鎖?
死鎖的發生必須知足如下四個條件:
互斥條件:一個資源每次只能被一個進程使用。
請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。
不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。
循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。
三種用於避免死鎖的技術:
加鎖順序(線程按照必定的順序加鎖)
加鎖時限(線程嘗試獲取鎖的時候加上必定的時限,超過期限則放棄對該鎖的請求,並釋放本身佔有的鎖)
死鎖檢測
(死鎖緣由及如何避免更深理解移步:http://blog.csdn.net/ls5718/article/details/51896159)
7,notify()和notifyAll()有什麼區別?
1,notify()和notifyAll()都是Object對象用於通知處在等待該對象的線程的方法。
2,void notify(): 喚醒一個正在等待該對象的線程。
3,void notifyAll(): 喚醒全部正在等待該對象的線程。
二者的最大區別在於:
notifyAll使全部原來在該對象上等待被notify的線程通通退出wait的狀態,變成等待該對象上的鎖,一旦該對象被解鎖,他們就會去競爭。
notify他只是選擇一個wait狀態線程進行通知,並使它得到該對象上的鎖,但不驚動其餘一樣在等待被該對象notify的線程們,當第一個線程運行完畢之後釋放對象上的鎖,此時若是該對象沒有再次使用notify語句,即使該對象已經空閒,其餘wait狀態等待的線程因爲沒有獲得該對象的通知,繼續處在wait狀態,直到這個對象發出一個notify或notifyAll,它們等待的是被notify或notifyAll,而不是鎖。
8,什麼是可重入鎖(ReentrantLock)?
Java.util.concurrent.lock 中的 Lock 框架是鎖定的一個抽象,它容許把鎖定的實現做爲Java 類,而不是做爲語言的特性來實現。這就爲Lock 的多種實現留下了空間,各類實現可能有不一樣的調度算法、性能特性或者鎖定語義。 ReentrantLock 類實現了Lock ,它擁有與synchronized 相同的併發性和內存語義,可是添加了相似鎖投票、定時鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭用狀況下更佳的性能。(換句話說,當許多線程都想訪問共享資源時,JVM能夠花更少的時候來調度線程,把更多時間用在執行線程上。)
Reentrant 鎖意味着什麼呢?簡單來講,它有一個與鎖相關的獲取計數器,若是擁有鎖的某個線程再次獲得鎖,那麼獲取計數器就加1,而後鎖須要被釋放兩次才能得到真正釋放。這模仿了synchronized 的語義;若是線程進入由線程已經擁有的監控器保護的synchronized 塊,就容許線程繼續進行,當線程退出第二個(或者後續)synchronized塊的時候,不釋放鎖,只有線程退出它進入的監控器保護的第一個synchronized 塊時,才釋放鎖。
9,讀寫鎖能夠用於什麼應用場景?
讀寫鎖能夠用於 「多讀少寫」 的場景,讀寫鎖支持多個讀操做併發執行,寫操做只能由一個線程來操做
ReadWriteLock對向數據結構相對不頻繁地寫入,可是有多個任務要常常讀取這個數據結構的這類狀況進行了優化。ReadWriteLock使得你能夠同時有多個讀取者,只要它們都不試圖寫入便可。若是寫鎖已經被其餘任務持有,那麼任何讀取者都不能訪問,直至這個寫鎖被釋放爲止。
ReadWriteLock 對程序性能的提升主要受制於以下幾個因素:
1,數據被讀取的頻率與被修改的頻率相比較的結果。
2,讀取和寫入的時間
3,有多少線程競爭
4,是否在多處理機器上運行
5,Java高併發鎖的3種實現 http://www.roncoo.com/article/detail/128506