回顧前面:html
只有光頭才能變強!java
本文章主要講的是Java多線程加鎖機制,有兩種:編程
不得不嘮叨幾句:c#
其實都比較坑,若是能先系統講了Synchronized鎖機制,接着講顯式Lock鎖機制,那就很容易理解了。也不須要跨那麼多章節。安全
那麼接下來咱們就開始吧~微信
synchronized是Java的一個關鍵字,它可以將代碼塊(方法)鎖起來多線程
public synchronized void test() { // 關注公衆號Java3y // doSomething }
synchronized是一種互斥鎖併發
synchronized是一種內置鎖/監視器鎖post
Java中的synchronized,經過使用內置鎖,來實現對變量的同步操做,進而實現了對變量操做的原子性和其餘線程對變量的可見性,從而確保了併發狀況下的線程安全。性能
咱們首先來看一段synchronized修飾方法和代碼塊的代碼:
public class Main { //修飾方法 public synchronized void test1(){ } public void test2(){ // 修飾代碼塊 synchronized (this){ } } }
來反編譯看一下:
同步代碼塊:
同步方法(在這看不出來須要看JVM底層實現)
synchronized底層是是經過monitor對象,對象有本身的對象頭,存儲了不少信息,其中一個信息標示是被哪一個線程持有。
具體可參考:
synchronized通常咱們用來修飾三種東西:
用的鎖是Java3y對象(內置鎖)
public class Java3y { // 修飾普通方法,此時用的鎖是Java3y對象(內置鎖) public synchronized void test() { // 關注公衆號Java3y // doSomething } }
用的鎖是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耦合了:
書上給出的辦法是使用組合的方式(也就是裝飾器模式)
獲取到的是類鎖(類的字節碼文件對象):Java3y.class
public class Java3y { // 修飾靜態方法代碼塊,靜態方法屬於類方法,它屬於這個類,獲取到的鎖是屬於類的鎖(類的字節碼文件對象)-->Java3y.class public synchronized void test() { // 關注公衆號Java3y // doSomething } }
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(); } }
結果證實:類鎖和對象鎖是不會衝突的!
咱們來看下面的代碼:
public class Widget { // 鎖住了 public synchronized void doSomething() { ... } } public class LoggingWidget extends Widget { // 鎖住了 public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); super.doSomething(); } }
doSomething()
方法時,此時拿到了LoggingWidget實例對象的鎖。doSomething()
方法,它又是被synchronized修飾。doSomething()
方法還須要一把鎖嗎?不須要的!
由於鎖的持有者是「線程」,而不是「調用」。線程A已是有了LoggingWidget實例對象的鎖了,當再須要的時候能夠繼續「開鎖」進去的!
這就是內置鎖的可重入性。
Lock顯式鎖是JDK1.5以後纔有的,以前咱們都是使用Synchronized鎖來使線程安全的~
Lock顯式鎖是一個接口,咱們來看看:
隨便翻譯一下他的頂部註釋,看看是幹嗎用的:
能夠簡單歸納一下:
前面說了,Lock顯式鎖給咱們的程序帶來了不少的靈活性,不少特性都是Synchronized鎖沒有的。那Synchronized鎖有沒有存在的必要??
必須是有的!!Lock鎖在剛出來的時候不少性能方面都比Synchronized鎖要好,可是從JDK1.6開始Synchronized鎖就作了各類的優化(畢竟親兒子,牛逼)
因此,到如今Lock鎖和Synchronized鎖的性能其實差異不是很大!而Synchronized鎖用起來又特別簡單。Lock鎖還得顧忌到它的特性,要手動釋放鎖才行(若是忘了釋放,這就是一個隱患)
因此說,咱們絕大部分時候仍是會使用Synchronized鎖,用到了Lock鎖說起的特性,帶來的靈活性纔會考慮使用Lock顯式鎖~
公平鎖理解起來很是簡單:
非公平鎖就是:
Lock和synchronize都是默認使用非公平鎖的。若是不是必要的狀況下,不要使用公平鎖
本文講了synchronized內置鎖和簡單描述了一下Lock顯式鎖,總得來講:
Lock鎖這裏只是介紹了一些些,明天會詳解它的相關子類和須要注意的地方,敬請期待~
以前在學習操做系統的時候根據《計算機操做系統-湯小丹》這本書也作了一點點筆記,都是比較淺顯的知識點。或許對你們有幫助
參考資料:
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y。爲了你們方便,剛新建了一下qq羣:742919422,你們也能夠去交流交流。謝謝支持了!但願能多介紹給其餘有須要的朋友
文章的目錄導航: