談談synchronized

爲何要用synchronized關鍵字:

  synchronized是java的一種內部鎖,是一種排他鎖,一般也被稱爲悲觀鎖,它可以保障原子性,可見性,有序性。java

  當多個線程去調用同一個方法的時候,若是不用加synchronized鎖,就可能出現線程不安全的問題。舉個經典的例子,好比兩夫妻一個用銀行卡,一個用網銀同時取同一個帳戶的錢,安全

取錢這個操做在銀行的後臺確定是一個方法,若是兩方同時調用,頗有可能形成取了兩份的錢,這樣確定是不行的。性能

synchronized的兩種使用方式:

  1,synchronized加在方法上this

public class T {

    private int count = 10;
    
    public synchronized void m() {
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

}

  2,synchronized代碼塊spa

public class T {

    private int count = 10;

    public void m() {
        synchronized(this) {
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    }

}

這裏兩種方式達到的效果是同樣的,咱們須要關注如下幾點:操作系統

  1,synchronized鎖,鎖的是什麼東西。實際上是鎖的一個對象,任意對象均可以(String常量,Integer,Long不能使用)。當一個線程拿到鎖以後,其餘的線程就只能等待當前線程執行完,線程

並釋放鎖以後,才能拿到鎖並執行。以此來保證線程的安全。code

  2,這兩種方式咱們該用哪種呢?實際開發中,咱們應該用代碼塊的方式,爲何要加鎖,一般都是須要訪問共享變量纔會加鎖,一個方法中並非全部代碼都須要訪問共享變量,對象

其餘的業務邏輯是不須要加鎖的,因此代碼塊的方式能夠提升程序的性能。blog

  3,synchronized是一種可重入的鎖,什麼意思呢,就是若是synchronized代碼塊中又調用了另一個加鎖的方法,原本若是鎖沒有釋放,是不能拿到鎖的。可是可重入鎖是能夠的,系統會自動識別。

synchronized的底層實現:

  jdk早期的時候,synchronized的底層實現是重量級的,重量到可能須要到操做系統去申請鎖的地步,因此形成synchronized的效率很是低。jdk1.5以後進行了改進,有了鎖升級的概念。

當咱們訪問synchronized的時候,HotSpot的實現是這樣的,當第一個線程來訪問的時候,先在鎖對象的頭上markword記錄這個線程,實際上只要一個線程來訪問的時候,是不會加鎖的,

只是記錄這個線程ID,此時稱之爲偏向鎖

  偏向鎖若是有線程競爭的話,好比我第一個線程尚未釋放鎖,第二個線程又來了,就會自動升級爲自旋鎖,自旋鎖的實現原理就是,線程會一直轉圈等待獲取鎖,若是轉圈十次以後,尚未獲取到鎖

就自動升級爲重量級鎖

  因此說從效率方面來說,CAS(後續文章會講解)並非必定就比synchronized鎖的效率高,理解synchronized的底層實現,咱們就能夠獲得以下結論:

  • 被鎖住的代碼,執行實際短,線程數量少的狀況,用CAS。
  • 被鎖住的代碼,執行時間長,線程數量多的狀況,用系統鎖(synchronized內部鎖和lock顯示鎖)。

  爲何這樣說呢,假如我有1000個線程,用CAS自旋,那豈不是有999個線程會一直在旋轉等待,這樣是很是消耗資源的。

相關文章
相關標籤/搜索