鎖在多線程編程中有很重要的做用,synchronized比較常見也很經常使用,可是Lock提供了更普遍的鎖操做,處理多線程同步的問題也更加優雅和靈活,Java從Java SE 5以後在併發包中提供Lock接口。java
一、類型不一樣
Lock是一個接口,是JDK層面的實現;synchronized是Java的關鍵字,是JVM層面的實現,是Java的內置特性;這也形成了在鎖管理上的不一樣。
二、釋放鎖的方式
Lock是在編碼中進行鎖的操做,須要主動調用接口中的方法釋放鎖,因此使用Lock時須要配合try模塊,在finally中釋放鎖,否則一旦發生異常極可能形成死鎖現象;synchronized因爲是JVM層面的實現,獲取鎖和釋放鎖的操做是不可見的,當發生異常時,鎖的釋放由JVM實現。
三、獲取鎖的過程
Lock接口中提供的方法,讓獲取鎖的過程更加靈活,編程人員能夠方便的在獲取鎖的過程當中進行多種操做,好比嘗試獲取鎖、設置獲取鎖的超時時間、中斷等待獲取鎖的線程等,應該說讓鎖的操做變得「豐富多彩」起來;synchronized是沒法這麼靈活的對鎖進行操做的。
四、效率
基於讀寫鎖接口的擴展,Lock能夠提升多個線程進行讀操做的效率,在大併發量的狀況下,效率的提升會尤爲明顯。編程
一、解決獲取鎖的等待問題
若是佔有鎖的線程A因爲各類緣由致使阻塞而沒有釋放鎖,此時其餘線程B也須要得到該鎖。synchronized的機制是讓B持續等待,若是A一直沒有釋放鎖,那麼B將一直等待,這會很大程度影響執行的效率;而Lock中提供了中斷線程等待的方法,也提供了帶有超時時間的獲取鎖的方法,後面會講到這些方法。
二、讀寫鎖的分離
咱們知道,多線程僅僅是執行讀操做的話是沒有衝突問題的,於是在讀操做時的鎖不必是獨佔的。synchronized實現同步就會致使在讀操做時只能有一個線程得到鎖,其餘線程只能等待鎖的釋放。Lock中的ReentrantReadWriteLock很好的解決了這個問題。
三、其餘鎖的操做
如獲知當前線程是否成功得到鎖等,synchronized是作不到的。多線程
先看一下Lock的源碼,它是一個接口:
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long var1, TimeUnit var3) throws InterruptedException; void unlock(); Condition newCondition(); }
在介紹每一個方法以前,先說明兩個注意事項:
一、Lock的實例通常定義爲成員變量,若是定義爲局部變量,每一個線程都會保存一個本身的副本,那麼在獲取鎖的操做時,實際每一個線程獲取的是不一樣的鎖,沒法造成同步互斥訪問。
二、獲取鎖的操做要放在try模塊以外,緣由是若是放在try模塊內的話,當獲取鎖的操做發生異常會調用finally中的代碼釋放鎖,而此時可能並無獲取鎖,就會拋出異常。
接下來看一下每一個方法的做用:
一、lock()
特色:發生異常不自動釋放鎖;若是沒有獲取到鎖會等待;
代碼示例:併發
二、tryLock()
特色:帶有boolean型返回值;不管是否成功獲取鎖會當即返回不進行等待;編碼
private Lock lock = new ......; //建立鎖 public void getLock() { if(lock.tryLock()) { try { System.out.print("業務處理"); //任務處理 } catch (Exception e) { } finally { lock.unlock(); //釋放鎖 } } else { System.out.print("獲取鎖失敗"); } }
三、tryLock(long time, TimeUnit unit)
特色:能夠設置獲取鎖的等待時間,如tryLock(4, TimeUnit.SECONDS)等待4秒;能夠在等待過程當中相應中斷;
示例代碼略,參考tryLock()代碼,要注意異常的處理。
四、lockInterruptibly()
特色:當一個使用該方法獲取鎖的線程沒有獲取到鎖處於阻塞等待狀態時,能夠調用線程的interrupt()方法中斷等待。
示例代碼略。spa
Lock接口有一個實現類ReentrantLock,便可重入鎖,除實現Lock接口的方法外,還提供了很是豐富的其餘的鎖操做方法,如公平鎖和非公平鎖。
ReadWriteLock是一個接口,接口代碼:
裏面只定義了兩個方法,一個獲取讀鎖,一個得到寫鎖,將資源的鎖分開爲兩個,從而使得資源能夠在多線程情境下併發讀操做。 ReentrantReadWriteLock類實現了ReadWriteLock接口,並提供了更多方法,最主要的仍是獲取讀寫鎖的方法。 讀寫鎖代碼示例:
public class LockTest { private ReadWriteLock lock = new ReentrantReadWriteLock(); //建立鎖 public static void main(String[] args) { final LockTest lockTest = new LockTest(); new Thread("A") { public void run() { lockTest.getLock(Thread.currentThread()); } }.start(); new Thread("B") { public void run() { lockTest.getLock(Thread.currentThread()); } }.start(); } public void getLock(Thread thread) { lock.readLock().lock(); //得到讀鎖 try { System.out.println("線程" + thread.getName() + "得到讀鎖"); long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() - startTime <= 1) { System.out.println("線程" + thread.getName() + "進行讀操做......"); } } catch (Exception e) { } finally { lock.readLock().unlock(); System.out.println("線程" + thread.getName() + "釋放讀鎖"); } } }
結果:
.net
一、可重入鎖
具有可重入性的鎖,即爲可重入鎖,好比synchronized和ReentrantLock都是。鎖基於線程分配,當某個線程得到了鎖去執行某個方法,方法中若是再次須要獲取鎖資源時,當前線程能夠直接得到鎖,沒必要從新申請。
二、可中斷鎖
能夠響應中斷的鎖。synchronized不是可中斷鎖,可是Lock是可中斷鎖。
三、公平鎖
儘可能按照請求鎖的順序得到鎖。synchronized是非公平鎖,ReentrantLock和ReentrantReadWriteLock提供了設置公平鎖的方法,不過默認爲非公平鎖。非公平鎖可能致使某些線程永遠獲取不到鎖。
四、讀寫鎖
將對資源的鎖分爲兩個,一個讀鎖和一個寫鎖,使得多個線程之間的讀操做不會發生衝突能夠並行,這極大的提升了讀的效率。不過讀鎖和寫鎖、寫鎖和寫鎖之間都是互斥的。線程