摘要:這樣看來(lái),和傳統(tǒng)的線程通信沒(méi)什么區(qū)別,的強(qiáng)大之處在于它可以為多個(gè)線程間建立不同的,下面引入中的一段代碼,加以說(shuō)明。
接近一周沒(méi)更新《Java線程》專欄了,主要是這周工作上比較忙,生活上也比較忙,呵呵,進(jìn)入正題,上一篇講述了并發(fā)包下的Lock,Lock可以更好的解決線程同步問(wèn)題,使之更面向?qū)ο?,并且ReadWriteLock在處理同步時(shí)更強(qiáng)大,那么同樣,線程間僅僅互斥是不夠的,還需要通信,本篇的內(nèi)容是基于上篇之上,使用Lock如何處理線程通信。
那么引入本篇的主角,Condition,Condition 將 Object 監(jiān)視器方法(wait、notify 和 notifyAll)分解成截然不同的對(duì)象,以便通過(guò)將這些對(duì)象與任意 Lock 實(shí)現(xiàn)組合使用,為每個(gè)對(duì)象提供多個(gè)等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和語(yǔ)句的使用,Condition 替代了 Object 監(jiān)視器方法的使用。下面將之前寫過(guò)的一個(gè)線程通信的例子替換成用Condition實(shí)現(xiàn)(Java線程(三)),代碼如下
public class ThreadTest2 { public static void main(String[] args) { final Business business = new Business(); new Thread(new Runnable() { @Override public void run() { threadExecute(business, "sub"); } }).start(); threadExecute(business, "main"); } public static void threadExecute(Business business, String threadType) { for(int i = 0; i < 100; i++) { try { if("main".equals(threadType)) { business.main(i); } else { business.sub(i); } } catch (InterruptedException e) { e.printStackTrace(); } } } } class Business { private boolean bool = true; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public /*synchronized*/ void main(int loop) throws InterruptedException { lock.lock(); try { while(bool) { condition.await();//this.wait(); } for(int i = 0; i < 100; i++) { System.out.println("main thread seq of " + i + ", loop of " + loop); } bool = true; condition.signal();//this.notify(); } finally { lock.unlock(); } } public /*synchronized*/ void sub(int loop) throws InterruptedException { lock.lock(); try { while(!bool) { condition.await();//this.wait(); } for(int i = 0; i < 10; i++) { System.out.println("sub thread seq of " + i + ", loop of " + loop); } bool = false; condition.signal();//this.notify(); } finally { lock.unlock(); } } }
在Condition中,用await()替換wait(),用signal()替換notify(),用signalAll()替換notifyAll(),傳統(tǒng)線程的通信方式,Condition都可以實(shí)現(xiàn),這里注意,Condition是被綁定到Lock上的,要?jiǎng)?chuàng)建一個(gè)Lock的Condition必須用newCondition()方法。
這樣看來(lái),Condition和傳統(tǒng)的線程通信沒(méi)什么區(qū)別,Condition的強(qiáng)大之處在于它可以為多個(gè)線程間建立不同的Condition,下面引入API中的一段代碼,加以說(shuō)明。
class BoundedBuffer { final Lock lock = new ReentrantLock();//鎖對(duì)象 final Condition notFull = lock.newCondition();//寫線程條件 final Condition notEmpty = lock.newCondition();//讀線程條件 final Object[] items = new Object[100];//緩存隊(duì)列 int putptr/*寫索引*/, takeptr/*讀索引*/, count/*隊(duì)列中存在的數(shù)據(jù)個(gè)數(shù)*/; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length)//如果隊(duì)列滿了 notFull.await();//阻塞寫線程 items[putptr] = x;//賦值 if (++putptr == items.length) putptr = 0;//如果寫索引寫到隊(duì)列的最后一個(gè)位置了,那么置為0 ++count;//個(gè)數(shù)++ notEmpty.signal();//喚醒讀線程 } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0)//如果隊(duì)列為空 notEmpty.await();//阻塞讀線程 Object x = items[takeptr];//取值 if (++takeptr == items.length) takeptr = 0;//如果讀索引讀到隊(duì)列的最后一個(gè)位置了,那么置為0 --count;//個(gè)數(shù)-- notFull.signal();//喚醒寫線程 return x; } finally { lock.unlock(); } } }
這是一個(gè)處于多線程工作環(huán)境下的緩存區(qū),緩存區(qū)提供了兩個(gè)方法,put和take,put是存數(shù)據(jù),take是取數(shù)據(jù),內(nèi)部有個(gè)緩存隊(duì)列,具體變量和方法說(shuō)明見(jiàn)代碼,這個(gè)緩存區(qū)類實(shí)現(xiàn)的功能:有多個(gè)線程往里面存數(shù)據(jù)和從里面取數(shù)據(jù),其緩存隊(duì)列(先進(jìn)先出后進(jìn)后出)能緩存的最大數(shù)值是100,多個(gè)線程間是互斥的,當(dāng)緩存隊(duì)列中存儲(chǔ)的值達(dá)到100時(shí),將寫線程阻塞,并喚醒讀線程,當(dāng)緩存隊(duì)列中存儲(chǔ)的值為0時(shí),將讀線程阻塞,并喚醒寫線程,這也是ArrayBlockingQueue的內(nèi)部實(shí)現(xiàn)。下面分析一下代碼的執(zhí)行過(guò)程:
1. 一個(gè)寫線程執(zhí)行,調(diào)用put方法; 2. 判斷count是否為100,顯然沒(méi)有100; 3. 繼續(xù)執(zhí)行,存入值; 4. 判斷當(dāng)前寫入的索引位置++后,是否和100相等,相等將寫入索引值變?yōu)?,并將count+1; 5. 僅喚醒讀線程阻塞隊(duì)列中的一個(gè); 6. 一個(gè)讀線程執(zhí)行,調(diào)用take方法; 7. …… 8. 僅喚醒寫線程阻塞隊(duì)列中的一個(gè)。
這就是多個(gè)Condition的強(qiáng)大之處,假設(shè)緩存隊(duì)列中已經(jīng)存滿,那么阻塞的肯定是寫線程,喚醒的肯定是讀線程,相反,阻塞的肯定是讀線程,喚醒的肯定是寫線程,那么假設(shè)只有一個(gè)Condition會(huì)有什么效果呢,緩存隊(duì)列中已經(jīng)存滿,這個(gè)Lock不知道喚醒的是讀線程還是寫線程了,如果喚醒的是讀線程,皆大歡喜,如果喚醒的是寫線程,那么線程剛被喚醒,又被阻塞了,這時(shí)又去喚醒,這樣就浪費(fèi)了很多時(shí)間。
[摘自高爽|Coder] 原創(chuàng)地址:http://blog.csdn.net/ghsau/ar...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/69839.html
摘要:提供了多線程升級(jí)方案將同步替換成了顯示的操作。線程間通信接口可以替代傳統(tǒng)的線程間通信,用替換,用替換,用替換。商品執(zhí)行上述代碼,觀察結(jié)果可以看到,多個(gè)線程同時(shí)生產(chǎn)消費(fèi),由于指定喚醒互異線程,因此并不會(huì)引起錯(cuò)誤。 JDK 1.5提供了多線程升級(jí)方案將同步synchronized替換成了顯示的Lock操作??梢詫?shí)現(xiàn)喚醒、凍結(jié)指定的線程。 Lock接口Lock 實(shí)現(xiàn)提供了比使用 synchr...
摘要:創(chuàng)建線程的方式方式一將類聲明為的子類。將該線程標(biāo)記為守護(hù)線程或用戶線程。其中方法隱含的線程為父線程。恢復(fù)線程,已過(guò)時(shí)。等待該線程銷毀終止。更多的使當(dāng)前線程在鎖存器倒計(jì)數(shù)至零之前一直等待,除非線 知識(shí)體系圖: showImg(https://segmentfault.com/img/bVbef6v?w=1280&h=960); 1、線程是什么? 線程是進(jìn)程中獨(dú)立運(yùn)行的子任務(wù)。 2、創(chuàng)建線...
摘要:為了拓展同步代碼塊中的監(jiān)視器鎖,開(kāi)始,出現(xiàn)了接口,它實(shí)現(xiàn)了可定時(shí)可輪詢與可中斷的鎖獲取操作,公平隊(duì)列,以及非塊結(jié)構(gòu)的鎖。 前言 系列文章目錄 前面幾篇我們學(xué)習(xí)了synchronized同步代碼塊,了解了java的內(nèi)置鎖,并學(xué)習(xí)了監(jiān)視器鎖的wait/notify機(jī)制。在大多數(shù)情況下,內(nèi)置鎖都能很好的工作,但它在功能上存在一些局限性,例如無(wú)法實(shí)現(xiàn)非阻塞結(jié)構(gòu)的加鎖規(guī)則等。為了拓展同步代...
閱讀 1730·2021-09-26 09:55
閱讀 5685·2021-09-22 15:40
閱讀 2073·2019-08-30 15:53
閱讀 1547·2019-08-30 11:15
閱讀 1765·2019-08-29 15:41
閱讀 1925·2019-08-28 18:13
閱讀 3207·2019-08-26 12:00
閱讀 1723·2019-08-26 10:30