多線程編程基礎--Java線程同步機制

1. 前言

從廣義來講,Java平臺提供的線程同步機制包括鎖、volatile關鍵字、final關鍵字、static關鍵字以及一些相關的API。本文主要介紹Java平臺中用於協調線程間共享數據訪問的相關關鍵字和API,固然也是經常使用的線程同步方法。java

2. 鎖概述

學習線程都知道,在多個線程併發訪問共享變量、共享資源時,會形成線程安全問題,那怎麼解決呢?編程

咱們很容易想到一種保障線程安全的方法--將多個線程對於併發訪問轉換爲串行訪問,即一個共享數據一次只能被一個線程訪問,該線程訪問結束後其餘線程才能對其進行訪問。鎖(Lock)就是利用這種思路以保障線程安全的線程同步機制。api

一些名詞概念:緩存

  • 臨界區: 鎖的持有線程在其得到鎖以後和釋放鎖以前這段時間內所執行的代碼稱做臨界區(Critical Section)。
  • 可重入性(Reentrancy): 一個線程在其持有一個鎖的時候可否再次或屢次申請該鎖。
  • 鎖的爭用與調度: 資源的爭用、調度的概念對鎖也是一樣適用的。
  • 鎖的粒度: 一個鎖實例所保護的共享數據的數量大小就被稱爲該鎖的粒度(Granularity)。數量大,稱鎖的粒度粗,不然,稱粒度細。

這些複雜概念就不深研究了,只簡單瞭解,想深刻研究的小夥伴自行查閱相關書籍。安全

3. 內部鎖:synchronized 關鍵字

Java平臺中的任何一個對象都有惟一一個與之關聯的鎖,這種鎖稱爲監視器(Monitor)或者內部鎖(Intrinsic Lock)。內部鎖可以保障原子性、可見性和有序性。內部鎖時經過synchronized 關鍵字實現的。synchronized提供了一種獨佔的加鎖方式,是比較經常使用的線程同步的關鍵字,通常在「線程安全的單例」中廣泛使用。該關鍵字可以保證代碼塊的同步性和方法層面的同步。多線程

代碼塊同步

synchronized 關鍵字修飾的代碼塊就被稱爲同步塊併發

//使用synchronized關鍵字實現線程安全的單例模式
    private static Singleton instance;
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class)
            {
                if(instance == null){
                    instance = new Singleton();
                }              
            }
        }
        return instance;
}
privateSingleton(){ }
複製代碼

方法同步

synchronized 關鍵字修飾的方法就被稱爲同步方法。 同步方法的這個方法體就是一個臨界區。負載均衡

public static synchronized Singleton getInstance2(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
private Singleton(){ }
複製代碼

4. 顯式鎖:Lock接口

以讀寫鎖爲例,Lock的使用方式很簡單,只須要在須要加鎖的地方先獲取鎖,操做完成以後釋放鎖,須要注意的是學習

要在finally中釋放鎖,這樣作的目的是保證在獲取到所以後,最終都是可以被釋放; 不要講獲取鎖的過程寫在try代碼塊中,防止在獲取鎖發生異常時致使的鎖無端釋放。spa

Lock lock  = new ReentrantLock();
lock.lock();
try{
//可能會出現線程安全的操做
}finally{
//必定在finally中釋放鎖
//也不能把獲取鎖在try中進行,由於有可能在獲取鎖的時候拋出異常
  lock.ublock();
}
複製代碼

Lock api Lock是一個接口,定義了鎖的獲取和釋放等基本操做。

  • void lock() 線程調用該方法獲取鎖,獲取鎖後返回;

  • void lockInterruptibly() throws InterruptedException 可中斷地獲取鎖,和lock()方法的區別在於該方法能夠響應中斷;

  • boolean tryLock() 嘗試非阻塞獲取鎖,線程調用該方法後馬上返回,成功獲取到鎖返回true,不然返回false;

  • boolean tryLock(long time, TimeUnit unit) throws InterruptedException 超時獲取鎖,該方法在如下3中狀況會返回: 在超時時間內得到鎖; 在超時時間被中斷; 超時時間結束仍未得到,返回false。

  • void unlock() 釋放鎖;

  • Condition newCondition() 獲取等待通知Condition組件,該組件和當前鎖綁定,只有線程獲取到了鎖才能調用await()方法,調用後,當前線程釋放鎖。

Lock與synchronized之間的區別和聯繫

在總結二者的區別和聯繫以前先引入兩個概念:隱式鎖和顯式鎖。 上面都有涉及,下面仔細講解一下。

隱式鎖: 隱式獲取鎖,synchronized是它的表明,使用者不須要關心其內部鎖的獲取和釋放,全部的鎖的相關操做都由具體的關鍵字完成; 顯式鎖: 顯示地獲取鎖,Lock是它的表明,須要使用者在使用的時候顯示地獲取和釋放鎖。

顯式鎖和隱式鎖都實現了對臨界區訪問的控制,可是顯式鎖提供了更靈活、更強大的接口:

  • 隱式鎖將鎖的獲取和釋放固化了,只能先獲取再釋放;顯式鎖顯然無此約束,能夠按照本身的須要來作鎖的釋放;
  • 顯式鎖提供了可中斷獲取鎖以及超時獲取鎖等多種隱式鎖不具有的同步特性;
  • 提供維度更小的等待與喚醒(Condition)。

就Lock接口提供的synchronized關鍵字不具有的特性作一個分析描述:

特性 描述
嘗試非阻塞獲取鎖 當前線程嘗試獲取鎖,若是鎖未被其餘線程獲取,當前線程成功獲取並持有鎖
可中斷獲取鎖 獲取鎖的線程可以響應中斷,當獲取到鎖的線程被中斷時,拋出中斷異常,鎖被釋放
超時獲取鎖 在指定的時間內獲取鎖,若是在指定的時間內未獲取到,獲取鎖失敗,返回

5. 輕量級同步機制:volatile 關鍵字

volatile 「不穩定」的意思。volatile關鍵字用於修飾共享可變變量,既沒有使用 final 關鍵字修飾的實例變量或靜態變量,相應的變量就被稱做 volatile變量。 private volatile int logLevel;

volatile 關鍵字表示被修飾的變量的值容易發生變化(即被其餘線程更改),於是不穩定。volatile變量的不穩定性意味着對這種變量的讀和寫都必須從高速緩存或者主內存中讀取,以讀取變量的相對新值。

volatile 關鍵字常被稱爲輕量級鎖,其做用與鎖的做用有相同的地方:保證可見性和有序性。所不一樣的是,在原子性方面它僅能保障寫volatile 變量操做的原子性,但沒有鎖 的排他性;其次,volatile 關鍵字的使用不會引發上下文切換(這是volatile 被稱爲輕量級的緣由)。

6. 小結&參考資料

小結

對於多線程編程來講,此文介紹的僅是九牛一毛,在解決負載均衡問題,充分利用CPU資源方面,多線程編程起着相當重要的做用。 我纔剛剛入門,加油,菜雞!!

參考資料

相關文章
相關標籤/搜索