Lock鎖介紹:
在java中可以使用 synchronized 來實現多線程下對象的同步訪問,為了獲得更加靈活使用場景、高效的性能,java還提供了Lock接口及其實現類ReentrantLock和讀寫鎖 ReentrantReadWriteLock。
相比synchronized來實現同步,使用Lock實現同步主要有以下差異性:
1、使用synchronized關鍵字時,鎖的控制和釋放是在synchronized同步代碼塊的開始和結束位置。而在使用Lock實現同步時,鎖的獲取和釋放可以在不同的代碼塊、不同的方法中。這一點是基於使用者手動獲取和釋放鎖的特性。
2、Lock接口提供了試圖獲取鎖的tryLock()方法,在調用tryLock()獲取鎖失敗時返回false,這樣線程可以執行其它的操作 而不至於使線程進入休眠。tryLock()方法可傳入一個long型的時間參數,允許在一定的時間內來獲取鎖。
3、Lock接口的實現類ReentrantReadWriteLock提供了讀鎖和寫鎖,允許多個線程獲得讀鎖、而只能有一個線程獲得寫鎖。讀鎖和寫鎖不能同時獲得。實現了讀和寫的分離,這一點在需要並發讀的應用中非常重要,如lucene允許多個線程讀取索引數據進行查詢但只能有一個線程負責索引數據的構建。
4、基於以上3點,lock來實現同步具備更好的性能。
Lock鎖與條件同步:
與synchronized類似,Lock鎖也可以實現條件同步。在java的concurrent包中提供了 Condition 接口及其實現類ConditionObject。
當滿足一定條件時,調用Condition的await()方法使當前線程進入休眠狀態進行等待。調用Condition的signalAll()方法喚醒因await()進入休眠的線程。
在synchronized與條件同步博文中,我們使用synchronized實現了一個生產者-消費者模型,在這里,來試試使用Lock鎖及其同步條件來實現同樣的一個生產者-消費者模型:
public class MessageStorageByLock { private int maxSize; private List<String> messages; private final ReentrantLock lock; private final Condition conditionWrite;//聲明兩個鎖條件 private final Condition conditionRead; public MessageStorageByLock(int maxSize) { this.maxSize = maxSize; messages = new LinkedList<String>(); lock = new ReentrantLock(true);//true修改鎖的公平性,為true時,使用lifo隊列來順序獲得鎖 conditionWrite = lock.newCondition();//調用newCondition()方法,即new ConditionObject(); conditionRead = lock.newCondition(); } public void set(String message){ //使用鎖實現同步,獲取所得操作,當鎖被其他線程占用時,當前線程將進入休眠 lock.lock(); try{ while(messages.size() == maxSize){ System.out.print("the message buffer is full now,start into wait()\n"); conditionWrite.await();//滿足條件時,線程休眠並釋放鎖。當調用 signalAll()時。線程喚醒並重新獲得鎖 } Thread.sleep(100); messages.add(message); System.out.print("add message:"+message+" success\n"); conditionRead.signalAll();//喚醒因conditionRead.await()休眠的線程 }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); } } public String get(){ String message = null; lock.lock(); try{ while(messages.size() == 0){ conditionRead.await(); System.out.print("the message buffer is empty now,start into wait()\n"); } Thread.sleep(100); message = ((LinkedList<String>)messages).poll(); System.out.print("get message:"+message+" success\n"); conditionWrite.signalAll(); }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); } return message; } }總結:
不管是synchronized關鍵字還是Lock鎖,都是用來在多線程的環境下對資源的同步訪問進行控制,用以避免因多個線程對數據的並發讀寫造成的數據混亂問題。與synchronized不同的是,Lock鎖實現同步時需要使用者手動控制鎖的獲取和釋放,其靈活性使得可以實現更復雜的多線程同步和更高的性能,但同時,使用者一定要在獲取鎖后及時捕獲代碼運行過程中的異常並在finally代碼塊中釋放鎖。