在Java多線程中,除了使用synchronize關鍵字來實現線程之間的同步互斥,還可以使用JDK1.5中新增的RetrantLock類來實現同樣的效果。RetrantLock類的擴展功能也更加強大,比如具有嗅探鎖定,多路分支通知等功能,在使用上也比synchronize更為靈活。
借助於Condition對象,RetrantLock可以實現類似於Object的wait和notify/notifyAll功能。使用它具有更好的靈活性,在一個Lock對象里面可以創建多個Condition(對象監視器)實例,線程對象可以注冊在指定的Condition對象中,從而可以有選擇性的進行線程通知,實現多路通知功能,在調度線程上更加靈活。
每一個 Lock 可以有任意數據的 Condition 對象, Condition 是與 Lock 綁定的,所以就有 Lock 的公平性特性:如果是公平鎖,線程為按照FIFO的順序從 Condition.await 中釋放,如果是非公平鎖,那么后續的鎖競爭就不保證FIFO順序了。
下面是一個生產者、消費者的示例:
/* * 買家線程,當書店中有書的時候買書 */ public class Buyer extends Thread { private BookStore bookStore; public Buyer(BookStore bookStore) { this.bookStore = bookStore; } @Override public void run() { while (true) { bookStore.removeBook(); } } }
/* * 售貨員線程,當書店中還有空位,買進書籍 */ public class Seller extends Thread { private BookStore bookStore; public Seller(BookStore bookStore) { this.bookStore = bookStore; } @Override public void run() { while (true) { bookStore.addBook(); } } }
import java.util.ArrayList; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /* * 書店類 */ public class BookStore { private ArrayList books = new ArrayList(); private ReentrantLock lock = new ReentrantLock(false); private Condition buyCondition = lock.newCondition(); private Condition sellCondition = lock.newCondition(); public void addBook() { lock.lock(); while (books.size() >= 1) { try { System.out.println(Thread.currentThread().getName() + "等待圖書售出"); sellCondition.await(); //售貨員等待書店出現空位 } catch (InterruptedException e) { e.printStackTrace(); } } books.add(1); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "購入了一本書,剩余:" + books.size()); buyCondition.signal(); //通知買家買書 lock.unlock(); } public void removeBook() { lock.lock(); while (books.size() <= 0) { try { System.out.println(Thread.currentThread().getName() + "等待購入圖書"); buyCondition.await(); //買家等待書店進書 } catch (InterruptedException e) { e.printStackTrace(); } } books.remove(0); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "買了一本書,剩余:" + books.size()); sellCondition.signal(); //通知售貨員進書 lock.unlock(); } }
/* * 測試類 */ public class Test { public static void main(String[] args) { BookStore bookStore = new BookStore(); Buyer[] buyers = new Buyer[5]; Seller sellers; //創建5個買家線程,負責買書 for(int i = 0; i < 5; i++) { buyers[i] = new Buyer(bookStore); } //創建售貨員線程,負責進書 sellers = new Seller(bookStore); //啟動線程 sellers.start(); for(int i = 0; i < 5; i++) { buyers[i].start(); } } }
運行結果:
從結果我們可以看到售貨員線程和買家線程是交替運行的,這就是因為兩類線程分別綁定了兩個不同的Condition:buyCondition,sellCondition。從而實現了兩類線程的交替喚醒。