這篇文章主要介紹“Java并發編程的知識點有哪些”,在日常操作中,相信很多人在Java并發編程的知識點有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java并發編程的知識點有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
創新互聯公司專注于企業營銷型網站建設、網站重做改版、乾安網站定制設計、自適應品牌網站建設、成都h5網站建設、購物商城網站建設、集團公司官網建設、外貿營銷網站建設、高端網站制作、響應式網頁設計等建站業務,價格優惠性價比高,為乾安等各大城市提供網站開發制作服務。
殺死一個開發,只需要變更三次需求。
2.1-volatile的應用(wall la tai l 還是 wall lei tai l)
它在多處理器開發中保證了共享變量的“可見性”??梢娦缘囊馑际钱斠粋€線程
修改一個共享變量時,另外一個線程能讀到這個修改的值,它不會引起線程上下文的切換和調度
CPU術語定義
volatile是如何來保證可見性的呢?讓我們在X86處理器下通過工具獲取JIT編譯器生成的
匯編指令來查看對volatile進行寫操作時,CPU會做什么事情。
Java代碼如下。
instance = new Singleton(); // instance是volatile變量
轉變成匯編代碼,如下。
0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);
Lock前綴的指令在多核處理器下會引發了兩件事情[1]。
1)將當前處理器緩存行的數據寫回到系統內存。
2)這個寫回內存的操作會使在其他CPU里緩存了該內存地址的數據無效。
2.volatile的使用優化
著名的Java并發編程大師Doug lea在JDK 7的并發包里新增一個隊列集合類Linked-
TransferQueue,它在使用volatile變量時,用一種追加字節的方式來優化隊列出隊和入隊的性
能。LinkedTransferQueue的代碼如下。
/** 隊列中的頭部節點 */private transient f?inal PaddedAtomicReference<QNode> head;/** 隊列中的尾部節點 */private transient f?inal PaddedAtomicReference<QNode> tail;static f?inal class PaddedAtomicReference <T> extends AtomicReference T> {// 使用很多4個字節的引用追加到64個字節 Object p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pa, pb, pc, pd, pe; PaddedAtomicReference(T r) {super(r); }}public class AtomicReference <V> implements java.io.Serializable {private volatile V value;// 省略其他代碼}
追加字節能優化性能?這種方式看起來很神奇,但如果深入理解處理器架構就能理解其中的奧秘。讓我們先來看看LinkedTransferQueue這個類,它使用一個內部類類型來定義隊列的頭節點(head)和尾節點(tail),而這個內部類PaddedAtomicReference相對于父類AtomicReference只做了一件事情,就是將共享變量追加到64字節。我們可以來計算下,一個對象的引用占4個字節,它追加了15個變量(共占60個字節),再加上父類的value變量,一共64個
字節。
為什么追加64字節能夠提高并發編程的效率呢?因為對于英特爾酷睿i7、酷睿、Atom和NetBurst,以及Core Solo和Pentium M處理器的L1、L2或L3緩存的高速緩存行是64個字節寬,不支持部分填充緩存行,這意味著,如果隊列的頭節點和尾節點都不足64字節的話,處理器會將它們都讀到同一個高速緩存行中,在多處理器下每個處理器都會緩存同樣的頭、尾節點,當一個處理器試圖修改頭節點時,會將整個緩存行鎖定,那么在緩存一致性機制的作用下,會導致其他處理器不能訪問自己高速緩存中的尾節點,而隊列的入隊和出隊操作則需要不停修改頭節點和尾節點,所以在多處理器的情況下將會嚴重影響到隊列的入隊和出隊效率。Doug lea使用追加到64字節的方式來填滿高速緩沖區的緩存行,避免頭節點和尾節點加載到同一個緩存行,使頭、尾節點在修改時不會互相鎖定。
那么是不是在使用volatile變量時都應該追加到64字節呢?不是的。在兩種場景下不應該
使用這種方式。
·緩存行非64字節寬的處理器。如P6系列和奔騰處理器,它們的L1和L2高速緩存行是32個字節寬。
·共享變量不會被頻繁地寫。因為使用追加字節的方式需要處理器讀取更多的字節到高速緩沖區,這本身就會帶來一定的性能消耗,如果共享變量不被頻繁寫的話,鎖的幾率也非常小,就沒必要通過追加字節的方式來避免相互鎖定。
不過這種追加字節的方式在Java 7下可能不生效,因為Java 7變得更加智慧,它會淘汰或重新排列無用字段,需要使用其他追加字節的方式。
2.2.1Java對象頭
synchronized用的鎖是存在Java對象頭里的。如果對象是數組類型,則虛擬機用3個字寬(Word)存儲對象頭,如果對象是非數組類型,則用2字寬存儲對象頭。在32位虛擬機中,1字寬等于4字節,即32bit
Java對象頭里的Mark Word里默認存儲對象的HashCode、分代年齡和鎖標記位
2.2.2 鎖的升級與對比
Java SE 1.6中,鎖一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀
態和重量級鎖狀態
鎖可以升級,但不可降級
1.偏向鎖(Biased Locking)
個人理解:偏向,偏心,如果在運行過程中,遇到了其他線程搶占鎖,則持有偏向鎖的線程會被掛起,此時會設置偏向鎖的標識
在大多數情況下,鎖總是由同一線程多次獲得,不存在多線程競爭,所以出現了偏向鎖。其目標就是在只有一個線程執行同步代碼塊時能夠提高性能。
鎖的撤銷以及鎖的關閉都是會產生開銷的
偏向鎖的適用場景
始終只有一個線程在執行同步塊,在它沒有執行完釋放鎖之前,沒有其它線程去執行同步塊,在鎖無競爭的情況下使用,一旦有了競爭就升級為輕量級鎖,升級為輕量級鎖的時候需要撤銷偏向鎖,撤銷偏向鎖的時候會導致stop the word操作;
2.輕量級鎖
偏向鎖運行在一個線程進入同步塊的情況下,當第二個線程加入鎖爭用的時候,偏向鎖就會升級為輕量級鎖;
(1)輕量級鎖加鎖
線程在執行同步塊之前,JVM會先在當前線程的棧楨中創建用于存儲鎖記錄的空間,并
將對象頭中的Mark Word復制到鎖記錄中,官方稱為Displaced Mark Word。然后線程嘗試使用
CAS將對象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,當前線程獲得鎖,如果失
敗,表示其他線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。
(2)輕量級鎖解鎖
輕量級解鎖時,會使用原子的CAS操作將Displaced Mark Word替換回到對象頭,如果成
功,則表示沒有競爭發生。如果失敗,表示當前鎖存在競爭,鎖就會膨脹成重量級鎖
鎖的優缺點的對比
重量級鎖Synchronized
特別記錄一下
當作用于靜態方法時,鎖住的是Class實例,又因為Class的相關數據存儲在永久帶PermGen(jdk1.8則是metaspace),永久帶是全局共享的,因此靜態方法鎖相當于類的一個全局鎖,會鎖所有調用該方法的線程;
從JVM規范中可以看到Synchonized在JVM里的實現原理,JVM基于進入和退出Monitor對象來實現方法同步和代碼塊同步,但兩者的實現細節不一樣。代碼塊同步是使用monitorenter和monitorexit指令實現的,而方法同步是使用另外一種方式實現的
總結
以下摘抄博客
https://blog.csdn.net/xiaobaixiongxiong/article/details/100933396
在所有的鎖都啟用的情況下線程進入臨界區時會先去獲取偏向鎖,如果已經存在偏向鎖了,則會嘗試獲取輕量級鎖,如果以上兩種都失敗,則啟用自旋鎖,如果自旋也沒有獲取到鎖,則使用重量級鎖,沒有獲取到鎖的線程阻塞掛起,直到持有鎖的線程執行完同步塊喚醒他們;
偏向鎖是在無鎖爭用的情況下使用的,也就是同步開在當前線程沒有執行完之前,沒有其它線程會執行該同步塊,一旦有了第二個線程的爭用,偏向鎖就會升級為輕量級鎖,一點有兩個以上線程爭用,就會升級為重量級鎖;
如果線程爭用激烈,那么應該禁用偏向鎖。
鎖優化
以上介紹的鎖不是我們代碼中能夠控制的,但是借鑒上面的思想,我們可以優化我們自己線程的加鎖操作;
減少鎖的時間
不需要同步執行的代碼,能不放在同步快里面執行就不要放在同步快內,可以讓鎖盡快釋放;
減少鎖的粒度
它的思想是將物理上的一個鎖,拆成邏輯上的多個鎖,增加并行度,從而降低鎖競爭。它的思想也是用空間來換時間;
---------摘抄完畢
自旋鎖 VS 適應性自旋鎖
阻塞或喚醒一個Java線程需要操作系統切換CPU狀態來完成,這種狀態轉換需要耗費處理器時間。如果同步代碼塊中的內容過于簡單,狀態轉換消耗的時間有可能比用戶代碼執行的時間還要長。
在許多場景中,同步資源的鎖定時間很短,為了這一小段時間去切換線程,線程掛起和恢復現場的花費可能會讓系統得不償失。如果物理機器有多個處理器,能夠讓兩個或以上的線程同時并行執行,我們就可以讓后面那個請求鎖的線程不放棄CPU的執行時間,看看持有鎖的線程是否很快就會釋放鎖。
而為了讓當前線程“稍等一下”,我們需讓當前線程進行自旋,如果在自旋完成后前面鎖定同步資源的線程已經釋放了鎖,那么當前線程就可以不必阻塞而是直接獲取同步資源,從而避免切換線程的開銷。這就是自旋鎖。
自旋鎖本身是有缺點的,它不能代替阻塞。自旋等待雖然避免了線程切換的開銷,但它要占用處理器時間。如果鎖被占用的時間很短,自旋等待的效果就會非常好。反之,如果鎖被占用的時間很長,那么自旋的線程只會白浪費處理器資源。所以,自旋等待的時間必須要有一定的限度,如果自旋超過了限定次數(默認是10次,可以使用-XX:PreBlockSpin來更改)沒有成功獲得鎖,就應當掛起線程。
自旋鎖的實現原理同樣也是CAS,AtomicInteger中調用unsafe進行自增操作的源碼中的do-while循環就是一個自旋操作,如果修改數值失敗則通過循環來執行自旋,直至修改成功。
自旋鎖在JDK1.4.2中引入,使用-XX:+UseSpinning來開啟。JDK 6中變為默認開啟,并且引入了自適應的自旋鎖(適應性自旋鎖)。
自適應意味著自旋的時間(次數)不再固定,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定。如果在同一個鎖對象上,自旋等待剛剛成功獲得過鎖,并且持有鎖的線程正在運行中,那么虛擬機就會認為這次自旋也是很有可能再次成功,進而它將允許自旋等待持續相對更長的時間。如果對于某個鎖,自旋很少成功獲得過,那在以后嘗試獲取這個鎖時將可能省略掉自旋過程,直接阻塞線程,避免浪費處理器資源。
在自旋鎖中 另有三種常見的鎖形式:TicketLock、CLHlock和MCSlock,本文中僅做名詞介紹,不做深入講解,感興趣的同學可以自行查閱相關資料。
---以上來源于美團技術博客
請解釋偏向鎖對 synchronized 與 ReentrantLock 的價值?
偏向鎖?
對synchronize有用
Java偏向鎖(Biased Locking)是Java6引入的一項多線程優化。
偏向鎖,顧名思義,它會偏向于第一個訪問鎖的線程,如果在運行過程中,同步鎖只有一個線程訪問,不存在多線程爭用的情況,則線程是不需要觸發同步的,這種情況下,就會給線程加一個偏向鎖。
如果在運行過程中,遇到了其他線程搶占鎖,則持有偏向鎖的線程會被掛起,JVM會消除它身上的偏向鎖,將鎖恢復到標準的輕量級鎖。
它通過消除資源無競爭情況下的同步原語,進一步提高了程序的運行性能。
ReentrantLock已經實現了偏向鎖
synchronized實際也是可重入的只不過是jvm層次的
到此,關于“Java并發編程的知識點有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注創新互聯網站,小編會繼續努力為大家帶來更多實用的文章!
網頁名稱:Java并發編程的知識點有哪些
URL標題:http://m.newbst.com/article14/iidgde.html
成都網站建設公司_創新互聯,為您提供域名注冊、建站公司、Google、網頁設計公司、微信公眾號、ChatGPT
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯