ReentrantLock、Condition及ReadWriteLock
先看一下ReentrantLock和Condition
-
以一個(gè)簡(jiǎn)單的ReentrantLock和Condition的例子為入口
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class ConditionDemo { public static void main(String[] args) { final ReentrantLock lock = new ReentrantLock(); final Condition condition = lock.newCondition(); Thread thread1 = new Thread(new Runnable() { public void run() { try { lock.lock(); // Thread1首先獲得鎖锄开,然后調(diào)用condition.await()等待信號(hào) System.out.println("Thread1 get lock, then wait for new signal"); condition.await(); // 當(dāng)Thread2中調(diào)用condition.signal()/signalAll()之后,Thread1被喚醒繼續(xù)執(zhí)行 System.out.println("Thread1 got new signal and continue..."); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println("Thread1 unlock"); } } }); thread1.start(); Thread thread2 = new Thread(new Runnable() { public void run() { lock.lock(); // Thread2獲得鎖 System.out.println("Thread2 get lock, then send a signal."); // 調(diào)用signalAll()通知所有等待狀態(tài)的線程 condition.signalAll();// 此時(shí)Thread1被喚醒,并開(kāi)始爭(zhēng)奪鎖,但是由于thread2還沒(méi)有釋放鎖莹妒,因此無(wú)法繼續(xù)運(yùn)行,thread1繼續(xù)阻塞 System.out.println("Thread2 sent a signal and continue"); try { Thread.sleep(1000);// 等待1秒鐘后再釋放鎖 } catch (InterruptedException e) { e.printStackTrace(); } lock.unlock(); // Thread2釋放鎖之后Thread1重新獲得鎖繼續(xù)運(yùn)行 System.out.println("Thread2 unlock"); } }); thread2.start(); } }
-
執(zhí)行結(jié)果
Thread1 get lock, then wait for new signal Thread2 get lock, then send a signal. Thread2 sent a signal and continue Thread2 unlock Thread1 got new signal and continue... Thread1 unlock
從上面的結(jié)果可以看出旨怠,thread1執(zhí)行之后首先獲得了鎖,但是隨后執(zhí)行了condition.await()之后又釋放了鎖進(jìn)入等待狀態(tài)蜈块。然后thread2執(zhí)行之后獲得鎖,再發(fā)送signal給thread1,但此時(shí)還沒(méi)有釋放鎖课锌,所以thread1依舊阻塞直到一秒后thread2釋放鎖thread1才能繼續(xù)運(yùn)行述雾。
-
因此Condition非常適合線程間的通信,下面以消費(fèi)者生產(chǎn)者的例子來(lái)說(shuō)明展示Condition的強(qiáng)大
地主家的傻兒子和慢慢收租的地主
-
銀行卡
public class BankCard { private Long balance = 1000000L; public BankCard(Long balance) { this.balance = balance; } public BankCard() { } public Long getBalance() { return balance; } public void setBalance(Long balance) { this.balance = balance; } }
-
傻兒子
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; public class StupidSon implements Runnable { private BankCard bankCard; private int id; private Lock lock; private Condition condition; public StupidSon(BankCard bankCard, int id, Lock lock, Condition condition) { this.bankCard = bankCard; this.id = id; this.lock = lock; this.condition = condition; } public void run() { try { while (true) { lock.lock(); // 傻兒子每次消費(fèi)1000匣掸,當(dāng)余額不足時(shí)等老父親賺錢存到銀行卡 while (bankCard.getBalance() - 1000 <= 0) { System.out.println("StupidSon" + id + ", $" + bankCard.getBalance() + " no more money, wait for father's signal.."); condition.await(); } bankCard.setBalance(bankCard.getBalance() - 1000); System.out.println("StupidSon" + id + " cost $1000, remains:" + bankCard.getBalance()); lock.unlock(); // 一秒鐘消費(fèi)一次 Thread.sleep(1000L); } } catch (Exception e) { e.printStackTrace(); lock.unlock(); } } }
-
老父親
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; public class HardFather implements Runnable { private BankCard bankCard; private Lock lock; private Condition condition; public HardFather(BankCard bankCard, Lock lock, Condition condition) { this.bankCard = bankCard; this.lock = lock; this.condition = condition; } public void run() { while (true) { lock.lock(); bankCard.setBalance(bankCard.getBalance() + 500); System.out.println("Father earn $500, remains:" + bankCard.getBalance()); if (bankCard.getBalance() > 1000 && bankCard.getBalance() < 2000) { // 當(dāng)金額超過(guò)1000之后告訴傻兒子們可以來(lái)拿錢了 condition.signalAll(); System.out.println("Money above 1000 again, sent signal."); } lock.unlock(); try { // 三秒存一次錢 Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } } } }
-
測(cè)試實(shí)例
public class Main { public static void main(String[] args) { // 初始化金額為1萬(wàn) BankCard bankCard = new BankCard(10000L); Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // 這里創(chuàng)建兩個(gè)兒子 StupidSon son1 = new StupidSon(bankCard, 1, lock, condition); StupidSon son2 = new StupidSon(bankCard, 2, lock, condition); // 一個(gè)父親橄杨,公用一個(gè)lock和同一個(gè)condition HardFather father = new HardFather(bankCard, lock, condition); // 放入線程池執(zhí)行 ExecutorService pool = Executors.newCachedThreadPool(); pool.submit(father); pool.submit(son1); pool.submit(son2); } }
-
執(zhí)行結(jié)果
Father earn $500, remains:10500 StupidSon1 cost $1000, remains:9500 StupidSon2 cost $1000, remains:8500 StupidSon1 cost $1000, remains:7500 StupidSon2 cost $1000, remains:6500 StupidSon1 cost $1000, remains:5500 StupidSon2 cost $1000, remains:4500 Father earn $500, remains:5000 StupidSon1 cost $1000, remains:4000 StupidSon2 cost $1000, remains:3000 StupidSon1 cost $1000, remains:2000 StupidSon2 cost $1000, remains:1000 StupidSon1, $1000 no more money, wait for father's signal.. StupidSon2, $1000 no more money, wait for father's signal.. Father earn $500, remains:1500 Money above 1000 again, sent signal. StupidSon1 cost $1000, remains:500 StupidSon2, $500 no more money, wait for father's signal.. StupidSon1, $500 no more money, wait for father's signal.. Father earn $500, remains:1000 Father earn $500, remains:1500 Money above 1000 again, sent signal. StupidSon2 cost $1000, remains:500 StupidSon1, $500 no more money, wait for father's signal.. StupidSon2, $500 no more money, wait for father's signal.. Father earn $500, remains:1000 Father earn $500, remains:1500 Money above 1000 again, sent signal. StupidSon1 cost $1000, remains:500 StupidSon2, $500 no more money, wait for father's signal..
從結(jié)果可以看出惯悠,當(dāng)金額少于1000之后兩個(gè)兒子都在等待父親的信號(hào),然后當(dāng)他們的父親存錢達(dá)到1500之后發(fā)送信號(hào)睁宰,這兩個(gè)兒子便開(kāi)始爭(zhēng)奪鎖然后消耗金額繼續(xù)等待刹前。這其實(shí)就是最基本的一個(gè)生產(chǎn)者消費(fèi)者問(wèn)題耍目,使用ReentrantLock和Conditon之后處理生產(chǎn)者消費(fèi)者問(wèn)題就變得非常簡(jiǎn)單沮榜。
下面來(lái)看一下讀寫(xiě)鎖ReadWriteLock
銀行卡使用上例中的就不貼代碼了
-
傻兒子如下
import java.util.concurrent.locks.ReadWriteLock; public class StupidSon implements Runnable { private ReadWriteLock lock; private int id; private BankCard bankCard; public StupidSon(ReadWriteLock lock, BankCard bankCard, int id) { this.lock = lock; this.bankCard = bankCard; this.id = id; } public void run() { int cost = 1000; while (true) { lock.writeLock().lock(); // 獲取寫(xiě)鎖,讀鎖和寫(xiě)鎖是互斥、寫(xiě)鎖和寫(xiě)鎖也是互斥的拒担,此時(shí)其他線程讀嘹屯、寫(xiě)鎖都將無(wú)法獲取只能等待 if (bankCard.getBalance() - cost < 0) { System.out.println("StupidSon" + id + " no more money"); lock.writeLock().unlock(); // 判斷金額不足之后釋放寫(xiě)鎖,跳出while循環(huán)結(jié)束線程 break; } bankCard.setBalance(bankCard.getBalance() - cost); System.out.println("StupidSon" + id + " cost $" + cost + ", remain:" + bankCard.getBalance()); try { // 等待500毫秒之后再釋放寫(xiě)鎖 System.out.print("StupidSon" + id + " wait for 500ms..."); Thread.sleep(500); // 修改金額之后釋放寫(xiě)鎖从撼,此時(shí)其他線程可以繼續(xù)競(jìng)爭(zhēng)獲取寫(xiě)鎖和讀鎖 lock.writeLock().unlock(); System.out.println("StupidSon" + id + "release writeLock."); } catch (InterruptedException e) { e.printStackTrace(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("StupidSon" + id + " exit."); } }
-
父母類 起到監(jiān)督金額的作用
import java.util.concurrent.locks.ReadWriteLock; public class Parents implements Runnable { private ReadWriteLock lock; private BankCard bankCard; private int type; public Parents(ReadWriteLock lock, BankCard bankCard, int type) { this.lock = lock; this.bankCard = bankCard; this.type = type; } public void run() { String mof = type == 1 ? "Mother" : "Father"; while (true) { lock.readLock().lock(); // 獲取讀鎖州弟, 讀鎖和讀鎖是不互斥的 也就是說(shuō)多個(gè)線程可以同時(shí)獲取讀鎖钧栖,但是讀寫(xiě)鎖是互斥的,此時(shí)寫(xiě)鎖無(wú)法被獲得只能等待 if (bankCard.getBalance() <= 0) { System.out.println(mof + " saw no more money. stop check!"); lock.readLock().unlock();// 這里一定要釋放讀鎖婆翔,否則會(huì)導(dǎo)致死鎖 break; } System.out.println(mof + " check balance:" + bankCard.getBalance()); try { // 等待500毫秒之后再釋放讀鎖 System.out.print(mof + " wait for 500ms..."); Thread.sleep(500); System.out.println(mof + " release readLock."); } catch (InterruptedException e) { e.printStackTrace(); } // 讀取結(jié)束后釋放讀鎖拯杠,寫(xiě)鎖可以被其他線程競(jìng)爭(zhēng)獲取到 lock.readLock().unlock(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(mof+" exit"); } }
-
測(cè)試
public class Main { public static void main(String[] args) { ReadWriteLock lock = new ReentrantReadWriteLock(); BankCard bankCard = new BankCard(10000L); // 初始化四個(gè)兒子和兩老 Runnable son = new StupidSon(lock, bankCard, 1); Runnable son2 = new StupidSon(lock, bankCard, 2); Runnable son3 = new StupidSon(lock, bankCard, 3); Runnable son4 = new StupidSon(lock, bankCard, 4); Runnable mother = new Parents(lock, bankCard, 1); Runnable father = new Parents(lock, bankCard, 2); ExecutorService threadPoolExecutor = Executors.newCachedThreadPool(); threadPoolExecutor.submit(son3); threadPoolExecutor.submit(son4); threadPoolExecutor.submit(son2); threadPoolExecutor.submit(mother); threadPoolExecutor.submit(father); threadPoolExecutor.submit(son); } }
-
運(yùn)行結(jié)果
StupidSon4 cost $1000, remain:9000 StupidSon4 wait for 500ms...StupidSon4release writeLock. StupidSon2 cost $1000, remain:8000 StupidSon2 wait for 500ms...StupidSon2release writeLock. StupidSon3 cost $1000, remain:7000 StupidSon3 wait for 500ms...StupidSon3release writeLock. Mother check balance:7000 // 從這里可以看出讀鎖不互斥,因?yàn)槟赣H未釋放讀鎖的時(shí)候父親依舊可以獲取讀鎖 Mother wait for 500ms...Father check balance:7000 Father wait for 500ms...Father release readLock. Mother release readLock. StupidSon1 cost $1000, remain:6000 StupidSon1 wait for 500ms...StupidSon1release writeLock. StupidSon4 cost $1000, remain:5000 StupidSon4 wait for 500ms...StupidSon4release writeLock. StupidSon2 cost $1000, remain:4000 StupidSon2 wait for 500ms...StupidSon2release writeLock. StupidSon3 cost $1000, remain:3000 StupidSon3 wait for 500ms...StupidSon3release writeLock. StupidSon4 cost $1000, remain:2000 StupidSon4 wait for 500ms...StupidSon4release writeLock. StupidSon2 cost $1000, remain:1000 StupidSon2 wait for 500ms...StupidSon2release writeLock. Mother check balance:1000 Mother wait for 500ms...Father check balance:1000 Father wait for 500ms...Father release readLock. Mother release readLock. StupidSon1 cost $1000, remain:0 StupidSon1 wait for 500ms...StupidSon1release writeLock. StupidSon3 no more money StupidSon3 exit. StupidSon4 no more money StupidSon4 exit. StupidSon2 no more money StupidSon2 exit. Father saw no more money. stop check! Father exit Mother saw no more money. stop check! Mother exit StupidSon1 no more money StupidSon1 exit.
由上面的結(jié)果可以很直觀的了解到讀寫(xiě)鎖互斥啃奴、寫(xiě)鎖也寫(xiě)鎖之間互斥潭陪、讀鎖和讀鎖之間不互斥。
讀寫(xiě)鎖的使用時(shí)一定要注意的是要及時(shí)釋放鎖最蕾,否則容易導(dǎo)致死鎖的發(fā)生依溯,最保險(xiǎn)的方式是將unlock代碼放在finally代碼塊中去