java必談多線程

來源:互聯網
上載者:User

程式語言作為開發軟體的工具,與電腦溝通交流的工具。每個人都有選擇工具的理由。我選擇java的理由是:強規範,保證了很多低級錯誤,難以發覺的錯誤在編譯期被檢查;嚴格的記憶體管理,對記憶體的訪問java是有嚴格的規範的,它可以防止數組下標越界,錯誤的記憶體訪問;高效能的記憶體回收行程,java在cms並發收集器出現後效能上了一個台階,而且java虛擬機器還在不斷的發展進步,java的效能也會得到極大的提高,java的效能問題逐漸成了過去式;多線程,多線程是java優勢所在,java語言在這方面也是下了很大的功夫,使java的多線程效能和安全性都如此強大。這些原因使我非常熱愛在java語言上的學習,研究。

本文主要介紹java多線程方面的知識。正如很多java程式員知道的那樣,java多線程確實涉及到很多注意事項。這篇博文主要是引出一些話題,對他們進行討論。如果要深入討論的話,每一個話題都足以用幾篇博文來講解。下面是將要出現的一些話題:

1,為什麼要多線程(多線程的優勢)

2,多線程有什麼問題(多線程要解決的問題)

3,java對多線程的規範(java解決多線程問題對jvm作出的規範)

4,java解決多線程問題的手段,包括(synchronized,ReentrantLock,volatile,final,ThreadLocal,棧封閉,JUC技術等)

關於第一個話題:

要使用多線程的理由有很多,總體來說有如下(可能不全):

1,盡量利用系統資源,減少任務完成時間。(如,在IO阻塞的時候可以讓出cpu,分布式運算 mapreduce)

2,多任務系統,有些事情需要並存執行。(如,需要響應使用者插斷要求)

3,需要同時為更多人提供服務,不能因為一個長任務完全佔用系統資源,使一些短任務也不能服務。(如,web伺服器)

4,一台伺服器可以有很多cpu(單個cpu也可以支援超執行緒),所以多線程是必然的趨勢。

關於第二個話題:

多線程下確實有很多不確定性。也很難像序列化執行那麼偵錯工具。問題可能要一定的已耗用時間,一定的操作順序才能產生。所以很多程式員在迴避這個問題,很害怕去使用多線程。所以多線程要解決的第一個問題就是心理畏懼問題。對於技術上,多線程主要有兩個問題要解決。這也是開發多線程程式時一定要思考的問題。

1,共用資料的可見度(一個線程寫入的資料,何時對於其他線程可見)。

2,共用資源的競爭問題(多個線程同時請求一個資源,誰先誰後,如計數問題,轉賬問題等)。

注意這裡都有共用二字,也就是需要在多線程中同時操作資源才可能出問題。下面討論下這兩個問題產生的原因和情景。

對於可見度問題:

cpu的執行速度快的驚人,以致於電腦執行速度的瓶頸在IO上。所以除了cpu寄存器緩衝外,還有L1 ,L2——在cpu上的獨立緩衝,L3——同一個cpu槽內共用的緩衝。然後才是主存,磁碟,網路等。在cpu需要訪問資料時,會依次沿著上面的緩衝讀寫資料。就拿寫來說,假設一份資料在主存中,它會被一層層載入到寄存器緩衝中來,假設cpu0要改變這份資料的值,會先寫到寄存器緩衝中,這時候資料並不會馬上去修改主存中的資料,如果第二個cpu又來讀這份資料,它不會裡面讀到之前的改變值而是讀到一個"髒"值。也就是第一個操作對於第二個操作不可見。而且這時候第二個cpu可能根據條件判斷,對這個值做出一些處理。由於不可見,會引發判斷是失效,丟失的更新等等問題。不可見問題還有可能由重排序引起。

對於資源競爭問題:

資源競爭問題其實很好理解,如對一個變數計數。由於計數要分為讀取,改變(依賴原來值做修改),替換三個步驟。如果多個線程不加任何限制的執行這個三個步驟,就會使其它步驟失效。還有HashMap擴容問題,印表機這些只能獨佔資源等等。這些都是資源競爭的一些問題。資源競爭問題遠沒有這麼簡單,特別是在高並發下,還可能引發一些硬體層面的問題。這個也是多線程的痛點。資源競爭問題也可能由重排序引起。

關於第三個話題:

java對多線程提出來了自己的一套JMM模型。這個模型主要是對可見度約定——happens-before原則。happens-before有如下約定:

(1)同一個線程中的每條指令都happens-before於出現在其後的任何一個指令。

(2)對一個監視器的解鎖happens-before於每一個後續對同一個監視器的加鎖。

(3)對volatile欄位的寫入操作happens-before於後續這個欄位的讀操作。

(4)對於final修飾的變數,對它的構造happens-before於對它的第一次訪問之前。

(5)Thread.start()的調用會happens-before於啟動線程裡面的動作。

(6)Thread中的所有動作都happens-before於其他線程檢查到此線程結束或者Thread.join()中返回或者Thread.isAlive()==false。

(7)一個線程A調用另一個另一個線程B的interrupt()都happens-before於線程A發現B被A中斷(B拋出異常或者A檢測到B的isInterrupted()或者interrupted())。

(8)一個物件建構函數的結束happens-before與該對象的finalizer的開始

(9)如果A動作happens-before於B動作,而B動作happens-before與C動作,那麼A動作happens-before於C動作

這些happens-before的約定解決了共用變數的可見度問題。對於共用資源競爭性主要是靠有鎖的synchronized和lock,和無鎖的cas操作保證。

關於第四個話題:

被final修飾的變數,是不可變對象(如果是一個參考型別,只是引用不可變,其值任然可變)。所以通過final修飾共用變數,可以大大減少多線程訪問的複雜度。因為不可變對象一定是安全執行緒的。構造不可變對象是一個好的編程習慣。

棧封閉,其實就是說沒有共用變數,把所有變數的分配都在本線程的方法棧內使用。不傳遞給其他線程使用。這其實是迴避了多線程共用問題,很多時候我們仍然需要共用的。

ThreadLocal,這是另一個迴避多線程共用變數的技術(只對本線程共用)。它把對象綁定到執行緒區域,Thread上的ThreadLocalMap中。這是一個弱鍵引用的map,鍵是ThreadLocal,value是具體本地線程共用的對象。所以這樣的對象有兩種方式被回收。第一ThreadLocal執行個體失去了強引用,在gc時可能會把它回收,第二這個線程失去了強引用,隨著線程的回收而回收。所以ThreadLocal是不會記憶體溢出的。使用remove去移除不需要的變數是一種好習慣。如果線程長時間運行ThreadLocal通常也是不會失去強引用的,所以這裡的內容得不到回收。

volatile變數主要是對可見度的約束,即它的寫入總能立即被下一個讀取的線程看到。但是它不保證原子操作。

synchronized是在對象或類上的內建鎖。在synchronized範圍內的代碼不能被重排序,並且讀取共用變數能夠看到它的最新狀態,寫入共用變數能夠對持有這個鎖的其它對象立即可見。

ReentrantLock提供了與synchronized相同的功能。它們的主要區別是

1,ReentrantLock必須自己釋放鎖。通常在finally釋放。

2,ReentrantLock可以通過參數控制多線程訪問的公平性。

3,ReentrantLock可以通過tryLock(),非阻塞的獲得鎖,也可以在指定等待時間內獲得鎖。synchronized一旦進入鎖競爭就一定得等到鎖,所以更容易產生死結。

4,ReentrantLock可以響應中斷,而synchronized是無法響應中斷的。這一點很重要!一個好的線程設計都需要響應中斷。

JUC的包很多主要是一些多線程工具。這裡主要說下cas操作。像AtomicXXXX主要是利用cas操作,而不是鎖。所以它的效率會高於有鎖的同步。cas操作又主要是又cpu的硬體支援。JUC的內容很多在這裡不單獨討論,有興趣的同學可以看這篇部落格,http://blog.csdn.net/xieyuooo/article/details/8594713

關於多線程的討論暫時就到此了,如有任何紕漏還請多多指出,謝謝!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.