Java高效併發

一段時間沒有回顧多線程相關知識了,雖然工做中會用到一些多線程的內容,但都偏向於基礎,今天重讀多線程相關內容,發現有些東西仍是須要注意下。這些通常是面試高頻問題奧。程序員

瞭解併發的內幕是一個高級程序員不可缺乏的課程面試

Java內存模型

注意,Java內存模型(JMM)和JVM運行時數據區不是同一個概念,還有一個概念是Java對象模型下次能夠單獨拿出來講。安全

  • JMM都是圍繞着原子性,可見性,有序性來說的
  • JMM定義了JVM如何與計算機的內存進行交互

線程對變量的全部操做都須要在工做內存中完成,不可直接操做主內存。
image.pngmarkdown

內存間的交互操做:
Lock,Unlock 主內存
Read,Write 主內存
Load,Store 工做內存的變量
Use,Assign 工做內存的變量多線程

更多關於JMM的信息查看,多線程之Java內存模型併發

Volatile

Volatile能夠說是Java虛擬機內提供的最輕量級同步機制,其只保證,可見性與有序性,不保證原子性。app

可見性:當一條線程修改了這個變量的值,新值對於其餘線程來講是能夠馬上得知的,另外兩個能夠實現可見性的關鍵字:Synchronizedfinalide

有序性:若是再本線程內觀察,全部的操做都是有序的,若是再一個線程中觀察另一個線程,那麼全部的操做都是無序的。性能

Java與線程

併發不必定依賴多線程,如PHP中常見的多進程併發。Java的Thread類全部關鍵方法都是聲明爲Native的,因此Java並無本身實現線程。優化

實現線程的三種方式:使用內核線程實現,使用用戶線程實現,和使用用戶線程加更加輕量級進程實現。

  1. 內核線程實現(KLT,Kernel-Level Thread)。程序通常不會直接使用內核線程,而是使用內核線程的一種高級接口,輕量級進程(LWP,Light Weight Process,LWP),先有內核線程,纔能有輕量級進程。

缺點:各類線程操做,如建立,析構,及同步須要進行系統調用,而系統調用的代價比較高,須要在用戶態和內核態中來回切換。消耗內核資源,一個系統支持輕量級的進程數量是有限制的。

  1. 用戶線程實現,廣義上說,一個線程只要不是內核線程,那就能夠任務是用戶線程。用戶線程徹底在用戶態完成,不用內核的幫助,能夠支持更大的線程數量。

缺點:沒有內核支持,各類操做都比較複雜。如今基本棄用了。

  1. 用戶線程 + 輕量級進程,綜合二者的有點,用戶進程與輕量級進程數量比是不定的。

線程調度

協同式調度:好處是實現簡單,切換操做對線程本身是可知的,沒有線程同步的問題,線程把本身的事情幹完以後才進行線程切換。

缺點:若是程序編寫不穩定,那麼系統不可控制。一個進程堅持不讓出CPU執行實現,就會致使系統崩潰。

搶佔式調度(Java默認調度):每一個線程由系統來分配執行和絃,線程的切換不禁線程來決定,當一個進程出現問題,系統能夠殺掉這個進程。

注意:並非線程的優先級越高,線程就必定會優先執行,只是說優先級高的線程更可能被選擇到。

Java線程狀態轉換

貼一張圖,好好記:

image.png

線程安全的實現方法

  • 互斥同步,加鎖,悲觀方案,保證共享數據同一時刻只有一個線程訪問。,互斥是因,同步是果。
  • 非阻塞同步,CAS,樂觀方案,先進行操做,若是沒有其餘線程也進行操做,那麼就操做成功了,若是有其它線程也在操做共享數據,那麼再重試。
  • 無同步方案,通常爲純代碼,有一些特性,如不依賴堆上的公用系統資源

鎖優化

  • 自旋鎖與自適應自旋
    假如共享數據只會持續很短的一段時間,爲了這段時間進行掛起和恢復線程並不值得,這時咱們可讓後面請求鎖的線程稍等一下,讓線程進行一個忙循環(自旋),這就是所謂的自旋鎖。

由於們有時不值得共享數據到底被鎖了多久,盲目的自旋可能致使性能的損失,JDK1.6以後,系統引入了自適應的自旋,及在一次共享數據被鎖定時,加入系統屢次得到自旋鎖,系統能夠容許線程自旋的次數更多時間更久一些。若是屢次沒有得到自旋鎖,那麼系統下次可能會省略掉自旋鎖。

  • 鎖消除
    對一些不可能存在共享數據競爭的鎖進行消除。

  • 鎖粗化
    有時候多個操做都對同一個對象加鎖,頻繁的加鎖也會影響性能,那麼系統就把鎖的同步範圍進行擴展。如StringBuffer()的多個append操做。

  • 偏向鎖
    能夠理解爲偏袒鎖,鎖會偏向於第一個得到它的線程,若是接下來的執行過程當中,該鎖沒有被其餘線程獲取,則持有偏向鎖的線程將永遠不在須要進行同步。

最後

瞭解併發的內幕是一個高級程序員不可缺乏的課程

參考

  • 《深刻理解JVM》
相關文章
相關標籤/搜索