執行緒的同步控制synchronized和lock的對比和區別
阿新 • • 發佈:2019-01-09
我們在面試的時候,時常被問到如何保證執行緒同步已經對共享資源的多執行緒程式設計。我們當然用同步程式碼塊,同步方法,又或者是用java提供的鎖機制來達到對共享資源變數的同步控制。
那麼我們什麼時候用synchronized,什麼時候用lock,以及他們的區別是什麼呢;
首先來說synchronized 是Java的關鍵字,是Java的內建特性,在JVM層面實現了對臨界資源的同步互斥訪問,通過對物件的標頭檔案來操作,從而達到加鎖和釋放鎖的目的。物件的標頭檔案如下圖:
那麼synchronized的缺點是啥呢:
1)不能響應中斷;
2)同一時刻不管是讀還是寫都只能有一個執行緒對共享資源操作,其他執行緒只能等待
3)鎖的釋放由虛擬機器來完成,不用人工干預,不過此即使缺點也是優點,優點是不用擔心會造成死鎖,缺點是由可能獲取到鎖的執行緒阻塞之後其他執行緒會一直等待,效能不高。
而lock介面的提出就是為了完善synchronized的不完美的,首先lock是基於jdk層面實現的介面,和虛擬機器層面不是一個概念;其次對於lock物件中的多個方法的呼叫,可以靈活控制對共享資源變數的操作,不管是讀操作還是寫操作;
lock介面的關係圖:
ReentrentLock物件和ReentrentReadWriteLock為我們日常開發中見到和用到比較多的兩個類;他們都是可重入的鎖,即當同一執行緒獲取到鎖之後,他在不釋放鎖的情況下,可以再次獲取到當前已經拿到的鎖,只需標記獲取到鎖的次數加一即可;
下面已ReentrentLock的使用為例,來說明如何對共享變數的控制;要求執行緒甲和執行緒乙各自輪詢新增數字到list集合中;假設說次數為3次;
package part6.jstack; import java.util.ArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Created by xq on 17/6/26. */ public class TestLock { private ArrayList<String> arrayList= new ArrayList<>(); Lock lock = new ReentrantLock(); public static void main(String[] args) { final TestLock test = new TestLock(); for (int i = 0; i < 3; i++) { final Integer count=i; new Thread("甲"){public void run() { test.insert(Thread.currentThread(),count); };}.start(); new Thread("乙"){public void run() { test.insert(Thread.currentThread(),count); };}.start(); } test.arrayList.stream().forEach(e->{ System.out.println(e); }); } public void insert(Thread thread,Integer count) { lock.lock(); try { //執行緒獲取到了鎖 for (int i = 0; i<5; i++) { arrayList.add("第"+count+"次"+"執行緒"+thread.getName()+i); } } catch (Exception e) { }finally { //執行緒釋放鎖 lock.unlock(); } } }
執行結果如下圖:
從結果中可以看出,在一個時刻只能有一個執行緒獲取到鎖並執行列印;
那麼lock和synchronized的區別對比如下:
1)synchronized 在成功完成功能或者丟擲異常時,虛擬機器會自動釋放執行緒佔有的鎖;而Lock物件在發生異常時,如果沒有主動呼叫unLock()方法去釋放鎖,則鎖物件會一直持有,因此使用Lock時需要在finally塊中釋放鎖;
2)lock介面鎖可以通過多種方法來嘗試獲取鎖包括立即返回是否成功的tryLock(),以及一直嘗試獲取的lock()方法和嘗試等待指定時間長度獲取的方法,相對靈活了許多比synchronized;
3) 通過在讀多,寫少的高併發情況下,我們用ReentrantReadWriteLock分別獲取讀鎖和寫鎖來提高系統的效能,因為讀鎖是共享鎖,即可以同時有多個執行緒讀取共享資源,而寫鎖則保證了對共享資源的修改只能是單執行緒的。