synchronized實現同步的基礎:java中每一個對象均可以做爲鎖。當線程試圖訪問同步代碼時,必須先得到對象鎖,退出或拋出異常時必須釋放鎖。java
表現形式爲:代碼塊同步和方法同步。緩存
public synchronized void method1(){}複製代碼
鎖住的是該對象的一個實例,當不一樣線程調用該實例對象中該同步方法,線程只有一個獲得鎖,其他被阻塞。但若是不一樣線程同時對該類的不一樣實例對象執行該同步方法,則不會阻塞,由於他們使用不一樣的鎖。bash
synchronized(this)( //ToDo}
或
synchronized(普通變量){ }複製代碼
同上markdown
public synchronized static void method3(){}複製代碼
鎖住的是該類,當不一樣線程調用該類的該static同步方法時,就只能有一個線程得到鎖,其他線程被阻塞。多線程
synchronized(Test.class){ //ToDo}
或
synchronized(靜態變量){ //ToDo}複製代碼
同上併發
鎖一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖和重量級鎖高併發
鎖能夠升級但不能降級,意味着偏向鎖升級爲輕量級鎖後不能降級爲偏向鎖。工具
大所述狀況下,鎖不只不存在多線程競爭,並且老是由同一線程屢次得到,爲了讓線程得到所的代價更低而引入偏向鎖。oop
當一個線程訪問同步代碼並獲取鎖時,會對對象頭和棧幀中的鎖記錄裏保存鎖偏向的線程ID,之後該線程再進入和退出同步鎖時,不須要進行CAS操做來加鎖和解鎖,而只是簡單地測試一下對象頭的Mark Word裏是否存儲執行當前線程的偏向鎖。性能
偏向鎖做用是:在沒有別的線程競爭的時候,一直偏向當前線程,當前線程能夠一直執行。
輕量級鎖,由偏向鎖升級而來。
偏向鎖運行在一個線程進入同步塊的狀況下,當第二個線程加入鎖爭用的時候,偏向鎖就會升級爲輕量級鎖。
輕量級鎖膨脹以後,就升級爲重量級鎖。
重量級鎖時依賴對象內部的monitor鎖來實現的,而monitor又依賴操做系統的MutexLock(互斥鎖)來實現的,因此重量級鎖也被稱爲互斥鎖(synchronized就是重量級鎖)。
偏向鎖
優勢:加鎖和解鎖不須要額外的消耗
缺點:線程存在競爭,會帶來額外的鎖撤銷的消耗
場景:單一線程訪問同步塊場景
輕量級鎖
優勢:競爭的線程不會阻塞,提升了程序的響應速度。
缺點:線程自旋時不釋放CPU
場景:追求響應時間,同步塊執行速度很是快。
重量級鎖
優勢:線程競爭不使用自旋,釋放CPU
缺點:線程阻塞,響應時間緩慢。
場景:追求吞吐量,同步塊執行速度較長。
Lock,鎖對象。在Lock接口出現以前,Java程序時靠synchronized關鍵字實現鎖功能。而在Java SE5.0以後併發包中新增了Lock接口來實現鎖的功能。
它能提供synchronized關鍵字相似的同步功能,但須要顯示得到鎖和釋放鎖,至於兩者區別後文補充。
Lock接口的主要方法:
執行該方法時,若鎖處於空閒狀態,當前線程將得到鎖。相反,若是鎖已經被其餘線程持有,則禁止當前線程得到鎖。
若鎖可用,則得到鎖,並當即返回true,不然返回false。
tryLock()和Lock()的區別在於:
tryLock()只是嘗試得到鎖,若鎖不可用,不會致使當前線程被禁止,當前線程仍然繼續往下執行代碼。
Lock()則是必定要得到鎖,若是鎖不可用,就一直等待,在未得到鎖以前,當前線程並不繼續向下自行。
執行該方法時,當前線程將釋放持有鎖,鎖只能由持有者釋放,若線程沒有持有鎖,則執行該方法,可能致使異常方式。
條件對象,得到等待通知組件。該組件和當前的鎖綁定,當前線程只有得到鎖,纔會調用該組件的await()方法,調用後,當前線程將釋放鎖。
Reentrantlock的使用很簡單,只須要顯示調用,得到同步鎖,釋放同步鎖便可。
ReentrantLock lock = new ReentrantLock(); //參數默認false,不公平鎖 ..................... try { lock.lock(); //若是被其它資源鎖定,會在此等待鎖釋放,達到暫停的效果 //操做 }catch(Exception e){ //異常處理 } finally { lock.unlock(); //釋放鎖 }複製代碼
reentrantlock鎖,在高併發的條件下使用的性能遠遠高於synchronized關鍵字。
而且reentratnlock公平和非公平鎖的隊列都是基於鎖內部維護的一個雙向鏈表,表結點Node的值就是每個請求當前鎖的線程。
ReadWriteLock接口中主要方法以下:
public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing */ Lock writeLock(); }複製代碼
ReadWriteLock管理一組鎖,一個是隻讀鎖,一個是寫鎖。
Java併發庫中ReentrantReadWriteLock實現了ReadWriteLock接口並添加了可重入的特性。
讀鎖在同一時刻容許有多個線程在訪問,可是寫進程訪問時,全部的讀線程和其餘寫進程都被阻塞。
讀寫鎖維護一對鎖,一個讀鎖和一個寫鎖,經過讀寫鎖分離,使得併發性相比通常的排它鎖有很大的提高。
ReentrantReadWriteLock讀寫鎖的幾個特性:
讀寫鎖例子(程序來源於網上:blog.csdn.net/canot/artic…
):
public class Cache{ static Map<String,Object> map = new HashMap<String,Object>(); static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); static Lock rLock = rwl.readLock(); static Lock wLock = rwl.writeLock(); //獲取一個key對應的value public static final Object get(String key){ r.lock(); try{ return map.get(key); }finally{ r.unlock(); } } //設置key對應的value並返回舊的value public static fianl Object put(String key,Object value){ w.lock(); try{ return map.put(key,value); }final{ w.unlock(); } } //清空緩存 public static fianl void clear(){ w.lock(); try{ map.clear(); } finally{ w.unlock(); } } }複製代碼
鎖降級是指寫鎖降級成爲讀鎖。若是當前線程持有寫鎖,而後將其釋放再獲取讀鎖的過程不能稱爲鎖降級。鎖降級指的在持有寫鎖的時候再獲取讀鎖,獲取到讀鎖後釋放以前寫鎖的過程稱爲鎖釋放。
(程序來源於:blog.csdn.net/qq_38737992…
)
public void work() { reentrantReadWriteLock.readLock().lock(); if (!update) { reentrantReadWriteLock.readLock().unlock(); // 鎖降級開始 reentrantReadWriteLock.writeLock().lock(); try { if (!update) { // 準備數據 ++index; try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } update = true; } reentrantReadWriteLock.readLock().lock(); } finally { reentrantReadWriteLock.writeLock().unlock(); // 鎖降級結束,降級爲讀鎖 } } try { // 使用數據 for (int i=0; i<5; i++) { try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + index); } } finally { reentrantReadWriteLock.readLock().unlock(); } } 複製代碼
synchronized是關鍵字,而Lock是接口
synchronized沒法判斷是否獲取鎖的狀態,Lock能夠判斷是否獲取到鎖
synchronized自動釋放鎖(a線程執行完同步代碼會自動釋放鎖,b線程執行過程當中發生異常會釋放鎖)
lock需在finally中手動釋放鎖(unlock()方法釋放鎖),不然容易形成線程死鎖。
synchronized關鍵字的兩個線程1和線程2,若當前線程1得到鎖,線程2等待,若是線程1阻塞,線程2會一直等待下去。
而lock鎖不必定會等待下去,若是嘗試得到不到鎖,線程能夠不用一直等待就結束了。
synchronized的鎖可重入、不可中斷、非公平。
lock鎖可重入,可中斷、可公平
lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少許的同步問題
不可重入鎖:自旋鎖、wait()、notify()、notifyAll()
不可重入鎖,即不可遞歸調用,遞歸調用會發生死鎖
reentrantLock擁有synchronized相同的併發性和內存語義,此外還多列鎖投票、定時鎖等候和中斷所等候
使用synchronized鎖,A不釋放,B將一直等待下去
使用reentrantlock鎖,A不釋放,B等待一段時間就會中斷等待,而幹別的事情。
synchronized是在JVM層面上實現的,不但能夠經過一些監控工具監控synchronized的鎖定,並且代碼執行時出現異常,JVM會走到哪一個釋放鎖定。可是Lock不行
在資源競爭不是很激烈的狀況下,synchronized的性能優於reentrantlock鎖,而競爭激烈的狀況下,synchronized的性能降低幾十倍,而reentrantlock的性能維持常態。
synchronized會屢次自旋,以得到鎖,在這個過程當中等待的線程不會被掛起,於是節省了掛起和喚醒的上下文切換的開銷
而reentrantlock,不會自旋,而是直接掛起
於是在線程併發量不大的狀況下,synchronized由於擁有自旋鎖、偏向鎖和輕量級鎖的緣由,不用將等待線程掛起,偏向鎖甚至不用自旋,因此在這種狀況下要比reenttrantlock高效。
默認狀況下synchronized非公平鎖;reentrantlock默認是非公平鎖。
一個Reentrantlock對象能夠同時綁定多個Condition對象,
而在synchronized中,鎖對象的wait()
和notify()
或notifyAll()
方法能夠實現一個隱含的條件。
若是要和多餘一個添加關聯的時候,synchronized就不得不額外地添加一個鎖,而Reentrantlock則無須這麼作只須要屢次調用new Condition()
方法便可。