從廣義來講,Java平臺提供的線程同步機制包括鎖、volatile關鍵字、final關鍵字、static關鍵字以及一些相關的API。本文主要介紹Java平臺中用於協調線程間共享數據訪問的相關關鍵字和API,固然也是經常使用的線程同步方法。java
學習線程都知道,在多個線程併發訪問共享變量、共享資源時,會形成線程安全問題,那怎麼解決呢?編程
咱們很容易想到一種保障線程安全的方法--將多個線程對於併發訪問轉換爲串行訪問,即一個共享數據一次只能被一個線程訪問,該線程訪問結束後其餘線程才能對其進行訪問。鎖(Lock)就是利用這種思路以保障線程安全的線程同步機制。api
一些名詞概念:緩存
這些複雜概念就不深研究了,只簡單瞭解,想深刻研究的小夥伴自行查閱相關書籍。安全
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(){ }
複製代碼
以讀寫鎖爲例,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()方法,調用後,當前線程釋放鎖。
在總結二者的區別和聯繫以前先引入兩個概念:隱式鎖和顯式鎖。 上面都有涉及,下面仔細講解一下。
隱式鎖: 隱式獲取鎖,synchronized是它的表明,使用者不須要關心其內部鎖的獲取和釋放,全部的鎖的相關操做都由具體的關鍵字完成; 顯式鎖: 顯示地獲取鎖,Lock是它的表明,須要使用者在使用的時候顯示地獲取和釋放鎖。
顯式鎖和隱式鎖都實現了對臨界區訪問的控制,可是顯式鎖提供了更靈活、更強大的接口:
就Lock接口提供的synchronized關鍵字不具有的特性作一個分析描述:
特性 | 描述 |
---|---|
嘗試非阻塞獲取鎖 | 當前線程嘗試獲取鎖,若是鎖未被其餘線程獲取,當前線程成功獲取並持有鎖 |
可中斷獲取鎖 | 獲取鎖的線程可以響應中斷,當獲取到鎖的線程被中斷時,拋出中斷異常,鎖被釋放 |
超時獲取鎖 | 在指定的時間內獲取鎖,若是在指定的時間內未獲取到,獲取鎖失敗,返回 |
volatile 「不穩定」的意思。volatile關鍵字用於修飾共享可變變量,既沒有使用 final 關鍵字修飾的實例變量或靜態變量,相應的變量就被稱做 volatile變量。 private volatile int logLevel;
volatile 關鍵字表示被修飾的變量的值容易發生變化(即被其餘線程更改),於是不穩定。volatile變量的不穩定性意味着對這種變量的讀和寫都必須從高速緩存或者主內存中讀取,以讀取變量的相對新值。
volatile 關鍵字常被稱爲輕量級鎖,其做用與鎖的做用有相同的地方:保證可見性和有序性。所不一樣的是,在原子性方面它僅能保障寫volatile 變量操做的原子性,但沒有鎖 的排他性;其次,volatile 關鍵字的使用不會引發上下文切換(這是volatile 被稱爲輕量級的緣由)。
對於多線程編程來講,此文介紹的僅是九牛一毛,在解決負載均衡問題,充分利用CPU資源方面,多線程編程起着相當重要的做用。 我纔剛剛入門,加油,菜雞!!