摘要:底層是是通過對象,對象有自己的對象頭,存儲了很多信息,其中一個信息標示是被哪個線程持有。當一個線程執行的代碼出現異常時,其所持有的鎖會自動釋放。
前言
回顧前面:
多線程三分鐘就可以入個門了!
Thread源碼剖析
多線程基礎必要知識點!看了學習多線程事半功倍
只有光頭才能變強!
本文章主要講的是Java多線程加鎖機制,有兩種:
Synchronized
顯式Lock
不得不嘮叨幾句:
在《Java核心技術卷 一》是先講比較難的顯式Lock,而再講的是比較簡單的Synchronized
而《Java并發編程實戰》在前4章零散地講解了Synchronized,將顯式Lock放到了13章
其實都比較坑,如果能先系統講了Synchronized鎖機制,接著講顯式Lock鎖機制,那就很容易理解了。也不需要跨那么多章節。
那么接下來我們就開始吧~
一、synchronized鎖 1.1synchronized鎖是什么?synchronized是Java的一個關鍵字,它能夠將代碼塊(方法)鎖起來
它使用起來是非常簡單的,只要在代碼塊(方法)添加關鍵字synchronized,即可以實現同步的功能~
public synchronized void test() { // 關注公眾號Java3y // doSomething }
synchronized是一種互斥鎖
一次只能允許一個線程進入被鎖住的代碼塊
synchronized是一種內置鎖/監視器鎖
Java中每個對象都有一個內置鎖(監視器,也可以理解成鎖標記),而synchronized就是使用對象的內置鎖(監視器)來將代碼塊(方法)鎖定的!
1.2synchronized用處是什么?synchronized保證了線程的原子性。(被保護的代碼塊是一次被執行的,沒有任何線程會同時訪問)
synchronized還保證了可見性。(當執行完synchronized之后,修改后的變量對其他的線程是可見的)
Java中的synchronized,通過使用內置鎖,來實現對變量的同步操作,進而實現了對變量操作的原子性和其他線程對變量的可見性,從而確保了并發情況下的線程安全。
1.3synchronized的原理我們首先來看一段synchronized修飾方法和代碼塊的代碼:
public class Main { //修飾方法 public synchronized void test1(){ } public void test2(){ // 修飾代碼塊 synchronized (this){ } } }
來反編譯看一下:
同步代碼塊:
monitorenter和monitorexit指令實現的
同步方法(在這看不出來需要看JVM底層實現)
方法修飾符上的ACC_SYNCHRONIZED實現。
synchronized底層是是通過monitor對象,對象有自己的對象頭,存儲了很多信息,其中一個信息標示是被哪個線程持有。
具體可參考:
https://blog.csdn.net/chenssy/article/details/54883355
https://blog.csdn.net/u012465296/article/details/53022317
1.4synchronized如何使用synchronized一般我們用來修飾三種東西:
修飾普通方法
修飾代碼塊
修飾靜態方法
1.4.1修飾普通方法:用的鎖是Java3y對象(內置鎖)
public class Java3y { // 修飾普通方法,此時用的鎖是Java3y對象(內置鎖) public synchronized void test() { // 關注公眾號Java3y // doSomething } }1.4.2修飾代碼塊:
用的鎖是Java3y對象(內置鎖)--->this
public class Java3y { public void test() { // 修飾代碼塊,此時用的鎖是Java3y對象(內置鎖)--->this synchronized (this){ // 關注公眾號Java3y // doSomething } } }
當然了,我們使用synchronized修飾代碼塊時未必使用this,還可以使用其他的對象(隨便一個對象都有一個內置鎖)
所以,我們可以這樣干:
public class Java3y { // 使用object作為鎖(任何對象都有對應的鎖標記,object也不例外) private Object object = new Object(); public void test() { // 修飾代碼塊,此時用的鎖是自己創建的鎖Object synchronized (object){ // 關注公眾號Java3y // doSomething } } }
上面那種方式(隨便使用一個對象作為鎖)在書上稱之為-->客戶端鎖,這是不建議使用的。
書上想要實現的功能是:給ArrayList添加一個putIfAbsent(),這需要是線程安全的。
假定直接添加synchronized是不可行的
使用客戶端鎖,會將當前的實現與原本的list耦合了:
書上給出的辦法是使用組合的方式(也就是裝飾器模式)
1.4.3修飾靜態方法獲取到的是類鎖(類的字節碼文件對象):Java3y.class
public class Java3y { // 修飾靜態方法代碼塊,靜態方法屬于類方法,它屬于這個類,獲取到的鎖是屬于類的鎖(類的字節碼文件對象)-->Java3y.class public synchronized void test() { // 關注公眾號Java3y // doSomething } }1.4.4類鎖與對象鎖
synchronized修飾靜態方法獲取的是類鎖(類的字節碼文件對象),synchronized修飾普通方法或代碼塊獲取的是對象鎖。
它倆是不沖突的,也就是說:獲取了類鎖的線程和獲取了對象鎖的線程是不沖突的!
public class SynchoronizedDemo { //synchronized修飾非靜態方法 public synchronized void function() throws InterruptedException { for (int i = 0; i <3; i++) { Thread.sleep(1000); System.out.println("function running..."); } } //synchronized修飾靜態方法 public static synchronized void staticFunction() throws InterruptedException { for (int i = 0; i < 3; i++) { Thread.sleep(1000); System.out.println("Static function running..."); } } public static void main(String[] args) { final SynchoronizedDemo demo = new SynchoronizedDemo(); // 創建線程執行靜態方法 Thread t1 = new Thread(() -> { try { staticFunction(); } catch (InterruptedException e) { e.printStackTrace(); } }); // 創建線程執行實例方法 Thread t2 = new Thread(() -> { try { demo.function(); } catch (InterruptedException e) { e.printStackTrace(); } }); // 啟動 t1.start(); t2.start(); } }
結果證明:類鎖和對象鎖是不會沖突的!
1.5重入鎖我們來看下面的代碼:
public class Widget { // 鎖住了 public synchronized void doSomething() { ... } } public class LoggingWidget extends Widget { // 鎖住了 public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); super.doSomething(); } }
當線程A進入到LoggingWidget的doSomething()方法時,此時拿到了LoggingWidget實例對象的鎖。
隨后在方法上又調用了父類Widget的doSomething()方法,它又是被synchronized修飾。
那現在我們LoggingWidget實例對象的鎖還沒有釋放,進入父類Widget的doSomething()方法還需要一把鎖嗎?
不需要的!
因為鎖的持有者是“線程”,而不是“調用”。線程A已經是有了LoggingWidget實例對象的鎖了,當再需要的時候可以繼續“開鎖”進去的!
這就是內置鎖的可重入性。
1.6釋放鎖的時機當方法(代碼塊)執行完畢后會自動釋放鎖,不需要做任何的操作。
當一個線程執行的代碼出現異常時,其所持有的鎖會自動釋放。
不會由于異常導致出現死鎖現象~
二、Lock顯式鎖 2.1Lock顯式鎖簡單介紹Lock顯式鎖是JDK1.5之后才有的,之前我們都是使用Synchronized鎖來使線程安全的~
Lock顯式鎖是一個接口,我們來看看:
隨便翻譯一下他的頂部注釋,看看是干嘛用的:
可以簡單概括一下:
Lock方式來獲取鎖支持中斷、超時不獲取、是非阻塞的
提高了語義化,哪里加鎖,哪里解鎖都得寫出來
Lock顯式鎖可以給我們帶來很好的靈活性,但同時我們必須手動釋放鎖
支持Condition條件對象
允許多個讀線程同時訪問共享資源
2.2synchronized鎖和Lock鎖使用哪個前面說了,Lock顯式鎖給我們的程序帶來了很多的靈活性,很多特性都是Synchronized鎖沒有的。那Synchronized鎖有沒有存在的必要??
必須是有的!!Lock鎖在剛出來的時候很多性能方面都比Synchronized鎖要好,但是從JDK1.6開始Synchronized鎖就做了各種的優化(畢竟親兒子,牛逼)
優化操作:適應自旋鎖,鎖消除,鎖粗化,輕量級鎖,偏向鎖。
詳情可參考:https://blog.csdn.net/chenssy/article/details/54883355
所以,到現在Lock鎖和Synchronized鎖的性能其實差別不是很大!而Synchronized鎖用起來又特別簡單。Lock鎖還得顧忌到它的特性,要手動釋放鎖才行(如果忘了釋放,這就是一個隱患)
所以說,我們絕大部分時候還是會使用Synchronized鎖,用到了Lock鎖提及的特性,帶來的靈活性才會考慮使用Lock顯式鎖~
2.3公平鎖公平鎖理解起來非常簡單:
線程將按照它們發出請求的順序來獲取鎖
非公平鎖就是:
線程發出請求的時可以“插隊”獲取鎖
Lock和synchronize都是默認使用非公平鎖的。如果不是必要的情況下,不要使用公平鎖
公平鎖會來帶一些性能的消耗的
四、最后本文講了synchronized內置鎖和簡單描述了一下Lock顯式鎖,總得來說:
synchronized好用,簡單,性能不差
沒有使用到Lock顯式鎖的特性就不要使用Lock鎖了。
Lock鎖這里只是介紹了一些些,明天會詳解它的相關子類和需要注意的地方,敬請期待~
之前在學習操作系統的時候根據《計算機操作系統-湯小丹》這本書也做了一點點筆記,都是比較淺顯的知識點。或許對大家有幫助
操作系統第一篇【引論】
操作系統第二篇【進程管理】
操作系統第三篇【線程】
操作系統第四篇【處理機調度】
操作系統第五篇【死鎖】
操作系統第六篇【存儲器管理】
操作系統第七篇【設備管理】
參考資料:
《Java核心技術卷一》
《Java并發編程實戰》
《計算機操作系統-湯小丹》
https://blog.csdn.net/panweiwei1994/article/details/78483167
http://www.cnblogs.com/dolphin0520/category/602384.html
https://blog.csdn.net/chenssy/article/category/3145247
https://blog.csdn.net/u012465296/article/details/53022317
https://www.cnblogs.com/wxd0108/p/5479442.html
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y。為了大家方便,剛新建了一下qq群:742919422,大家也可以去交流交流。謝謝支持了!希望能多介紹給其他有需要的朋友
文章的目錄導航:
https://zhongfucheng.bitcron.com/post/shou-ji/wen-zhang-dao-hang
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71175.html
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區分點在于工作方向的側重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內容分為三個模塊 基礎模塊: 技術崗位與面試 計算機基礎 JVM原理 多線程 設計模式 數據結構與算法 應用模塊: 常用工具集 ...
摘要:當一個線程持有重量級鎖時,另外一個線程就會被直接踢到同步隊列中等待。 java代碼先編譯成字節碼,字節碼最后編譯成cpu指令,因此Java的多線程實現最終依賴于jvm和cpu的實現 synchronized和volatile 我們先來討論一下volatile關鍵字的作用以及實現機制,每個線程看到的用volatile修飾的變量的值都是最新的,更深入的解釋就涉及到Java的內存模型了,我們...
摘要:用戶態不能干擾內核態所以指令就有兩種特權指令和非特權指令不同的狀態對應不同的指令。非特權指令所有程序均可直接使用。用戶態常態目態執行非特權指令。 這是我今年從三月份開始,主要的大廠面試經過,有些企業面試的還沒來得及整理,可能有些沒有帶答案就發出來了,還請各位先思考如果是你怎么回答面試官?這篇文章會持續更新,請各位持續關注,希望對你有所幫助! 面試清單 平安產險 飛豬 上汽大通 浩鯨科...
摘要:導讀閱讀本文需要有足夠的時間,筆者會由淺到深帶你一步一步了解一個資深架構師所要掌握的各類知識點,你也可以按照文章中所列的知識體系對比自身,對自己進行查漏補缺,覺得本文對你有幫助的話,可以點贊關注一下。目錄一基礎篇二進階篇三高級篇四架構篇五擴 導讀:閱讀本文需要有足夠的時間,筆者會由淺到深帶你一步一步了解一個資深架構師所要掌握的各類知識點,你也可以按照文章中所列的知識體系對比自身,對自己...
閱讀 1169·2021-09-10 10:51
閱讀 903·2019-08-30 15:53
閱讀 2728·2019-08-30 12:50
閱讀 982·2019-08-30 11:07
閱讀 1996·2019-08-30 10:50
閱讀 3601·2019-08-29 18:47
閱讀 1314·2019-08-29 18:44
閱讀 1602·2019-08-29 17:01