自從Java提供了多線程編程,咱們常常須要處理這樣的狀況:在特定的時間,咱們須要限制訪問,確保只有一個線程訪問咱們的代碼。Java提供了同步關鍵字synchronized來實現這種訪問控制,可是使用synchronized會存在一些問題。第一個問題是,當咱們須要調用線程的wait()方法時,咱們必須記得去使用while循環。看下面例子,來自guava monitor api上的說明:java
public class SafeBox<V> { private V value; public synchronized V get() throws InterruptedException { while (value == null) { wait(); } V result = value; value = null; notifyAll(); return result; } public synchronized void set(V newValue) throws InterruptedException { while (value != null) { wait(); } value = newValue; notifyAll(); } }
在這個例子中獲取一個值,當值不存在的時候,咱們等待。。。有值的時候須要notifyAll()。這裏須要注意的是,咱們要在while循環中使用wait方法,而不是if。另外用notifyAll而不是notify。編程
在java.util.concurrent包中提供了ReentrantLock,咱們使用它來實現上面的場景看看api
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class SafeBox<V> { private final ReentrantLock lock = new ReentrantLock(); private final Condition valuePresent = lock.newCondition(); private final Condition valueAbsent = lock.newCondition(); private V value; public V get() throws InterruptedException { lock.lock(); try { while (value == null) { valuePresent.await(); } V result = value; value = null; valueAbsent.signal(); return result; } finally { lock.unlock(); } } public void set(V newValue) throws InterruptedException { lock.lock(); try { while (value != null) { valueAbsent.await(); } value = newValue; valuePresent.signal(); } finally { lock.unlock(); } } }
咱們依然須要使用while循環,可是有一個好處,咱們能夠定義兩個Condition,這樣咱們就能夠用signal來替代signalAll,這樣可能會帶來一點性能上的提高。多線程
Monitor是一個支持任意布爾條件的同步的抽象,Monitor類是做爲ReentrantLock的一個替代,代碼中使用Monitor比使用ReentrantLock更不易出錯,可讀性也更強,而且也沒有顯著的性能損失,使用Monitor甚至有潛在的性能獲得優化,下面咱們看看用guava中的Monitor怎麼重寫上面的代碼性能
package com.hupengcool.guava.concurrency; import com.google.common.util.concurrent.Monitor; public class SafeBox<V> { private final Monitor monitor = new Monitor(); private final Monitor.Guard valuePresent = new Monitor.Guard(monitor) { public boolean isSatisfied() { return value != null; } }; private final Monitor.Guard valueAbsent = new Monitor.Guard(monitor) { public boolean isSatisfied() { return value == null; } }; private V value; public V get() throws InterruptedException { monitor.enterWhen(valuePresent); try { V result = value; value = null; return result; } finally { monitor.leave(); } } public void set(V newValue) throws InterruptedException { monitor.enterWhen(valueAbsent); try { value = newValue; } finally { monitor.leave(); } } }
能夠發現使用guava以後,咱們不須要使用while,使用Monitor.Guard定義進入代碼快的條件便可,代碼變得更加容易閱讀,寫起來也更加方便。
當咱們Monitor的方法返回boolean值的時候,咱們在if塊中包含try-finally塊,確保鎖可以釋放。優化
if(monitor.enterIf(guard)){ try{ ...work.. }finally{ monitor.leave(); } }else{ .. monitor not available.. }
當monitor的方法不返回任何值的時候,咱們的代碼也須要在finally中釋放鎖google
monitor.enter() try{ ...work.. }finally{ monitor.leave(); }
Monitor有幾個經常使用的方法線程