您好,登錄后才能下訂單哦!
這篇文章主要介紹“JUC怎么模擬AND型信號(hào)量”,在日常操作中,相信很多人在JUC怎么模擬AND型信號(hào)量問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”JUC怎么模擬AND型信號(hào)量”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
1.一個(gè)錯(cuò)誤示例
在這里,首先解釋一下,為了滿足線程申請(qǐng)信號(hào)量不成功后將進(jìn)程阻塞,并插入到對(duì)應(yīng)的隊(duì)列中,所以使用了ReentrantLock+Condition來(lái)實(shí)現(xiàn)Swait方法。廢話不多說(shuō),直接上代碼:
//數(shù)據(jù)定義 static Lock lock = new ReentrantLock(); static Condition condition1 = lock.newCondition(); static Condition condition2 = lock.newCondition(); public static void Swait(String id, Semaphore s1, Semaphore s2) throws InterruptedException { lock.tryLock(1, TimeUnit.SECONDS); log.info("當(dāng)前的兩個(gè)信號(hào)量的狀態(tài):【{},{}】", s1.availablePermits(), s2.availablePermits()); //availablePermits可獲取到信號(hào)量中還剩余的值 if(s1.availablePermits() < 1 || s2.availablePermits() < 1){ if (s1.availablePermits() < 1) { log.info("線程【{}】被掛起到信號(hào)量【{}】中", id, s1); //阻塞,并插入到condition1的阻塞隊(duì)列中 condition1.await(); } else { log.info("線程【{}】被掛起到信號(hào)量【{}】中", id, s2); //阻塞,并插入到condition2的阻塞隊(duì)列中 condition2.await(); } log.info("被掛起的線程【{}】被喚醒執(zhí)行。", id); } else { log.info("為線程【{}】分配資源!", id); s1.acquire(); s2.acquire(); } lock.unlock(); } public static void Ssignal(Semaphore s1, Semaphore s2) throws InterruptedException { log.info("線程【{}】執(zhí)行了釋放資源", id); lock.tryLock(1, TimeUnit.SECONDS); s1.release(); s2.release(); //喚醒等待隊(duì)列中的線程 condition.signal(); lock.unlock(); }
大家仔細(xì)看上面的代碼,這個(gè)也是我剛開(kāi)始寫的代碼,第一眼看似乎是沒(méi)什么問(wèn)題,但是里面隱藏著一個(gè)坑,在Swait方法中,調(diào)用condition1.await(),此時(shí)線程被阻塞在這一行中,但是當(dāng)被別的線程(調(diào)用Ssignal)喚醒時(shí),在被阻塞的下一行開(kāi)始繼續(xù)執(zhí)行,但是在后續(xù)的代碼里,是沒(méi)有去申請(qǐng)信號(hào)量的,而是直接就Swait成功了,這樣在執(zhí)行Ssignal時(shí)就會(huì)導(dǎo)致信號(hào)量憑空的增加了,也就無(wú)法正確的表征系統(tǒng)中的資源數(shù)量了。
2.一個(gè)簡(jiǎn)單的示例
下面我們就對(duì)代碼進(jìn)行優(yōu)化,大家可以回顧一下AND型信號(hào)量,當(dāng)其因?yàn)橘Y源不足時(shí),需要將線程插入到第一個(gè)無(wú)法滿足條件(即Si<1)的信號(hào)量對(duì)應(yīng)的等待隊(duì)列中,并且將程序計(jì)數(shù)器放置到Swait操作的開(kāi)始處,所以我們對(duì)Swait代碼進(jìn)行修改如下:
public static void Swait(String id, Semaphore s1, Semaphore s2) throws InterruptedException { lock.tryLock(1, TimeUnit.SECONDS); log.info("當(dāng)前的兩個(gè)信號(hào)量的狀態(tài):【{},{}】", s1.availablePermits(), s2.availablePermits()); //如果申請(qǐng)不到,就掛起線程,并將線程插入到condition的隊(duì)列中 while (s1.availablePermits() < 1 || s2.availablePermits() < 1) { if (s1.availablePermits() < 1) { log.info("線程【{}】被掛起到信號(hào)量【{}】中", id, s1); condition1.await(); } else { log.info("線程【{}】被掛起到信號(hào)量【{}】中", id, s2); condition2.await(); } log.info("被掛起的線程【{}】被喚醒執(zhí)行。", id); } log.info("為線程【{}】分配資源!", id); s1.acquire(); s2.acquire(); lock.unlock(); }
在上面的代碼中,我們將請(qǐng)求的資源放到一個(gè)循環(huán)條件中,以滿足將程序計(jì)數(shù)器放置到Swait操作的開(kāi)始處,在每次被喚醒后都要重新判斷資源是否足夠,如果足夠才跳出循環(huán),否則就再次自我阻塞。
3.一個(gè)可以同時(shí)申請(qǐng)N個(gè)的Swait操作
如果你知道了信號(hào)量的種類數(shù)(系統(tǒng)中的資源類型),其實(shí)上面的代碼已經(jīng)可以滿足一定的需要了,只需要我們將所有的信號(hào)量寫入到參數(shù)列表中即可。但是對(duì)于致力于代碼的復(fù)用,這里就有些差強(qiáng)人意了,因此我們?cè)俅螌?duì)代碼進(jìn)行改進(jìn),代碼如下所示:
public static void Swait(String id, Semaphore... list) throws InterruptedException { lock.lock(); //如果資源不足,就掛起線程,并將線程插入到condition的隊(duì)列中 while (true) { int count=0; //循環(huán)判斷參數(shù)列表中信號(hào)量的可用值 for (Semaphore semaphore:list){ if(semaphore.availablePermits()>0){ count++; } } //如果資源都滿足,則跳出循環(huán),進(jìn)行資源分配 if(count == list.length){ break; } log.info("線程【{}】被掛起-----", id); //將當(dāng)前線程阻塞 condition1.await(); log.info("被掛起的線程【{}】被喚醒執(zhí)行。", id); } log.info("為線程【{}】分配資源!", id); //分配資源 for (Semaphore semaphore:list){ semaphore.acquire(); } lock.unlock(); } public static void Ssignal(String id, Semaphore... list) throws InterruptedException { log.info("線程【{}】執(zhí)行了釋放資源", id); lock.tryLock(1, TimeUnit.SECONDS); //循環(huán)釋放信號(hào)量 for (Semaphore semaphore:list){ semaphore.release(); } //喚醒等待隊(duì)列中的線程 condition.signal(); lock.unlock(); }
為此,我們將方法中的信號(hào)量列表改為可變的參數(shù)列表,這樣在傳參的時(shí)候就可以方便的進(jìn)行了,但是也會(huì)存才一些問(wèn)題,比如無(wú)法約束“借出”與“歸還”的信號(hào)量的數(shù)量是否一致。并且因?yàn)樾盘?hào)量的數(shù)量不定,所以無(wú)法為每個(gè)信號(hào)量新建一個(gè)條件變量(Condition),因此在上面的代碼中所有的信號(hào)量公用一個(gè)條件變量,所有阻塞的線程都插入在其阻塞隊(duì)列中。
4.一個(gè)完整的例子
這里我們使用一個(gè)經(jīng)典的進(jìn)程同步問(wèn)題來(lái)演示我們使用Java模擬的AND型信號(hào)量,在這里,我們采用生產(chǎn)者–消費(fèi)者問(wèn)題來(lái)演示,完整的代碼如下:
//用來(lái)保證互斥的訪問(wèn)臨界區(qū)(緩存區(qū)) static final Semaphore mutex = new Semaphore(1); //緩沖區(qū),最大容量為50 static List<Integer> buffer = new ArrayList<>(); //緩沖區(qū)中還可放入的消息數(shù)量 static final Semaphore empty = new Semaphore(50); //緩沖區(qū)中的消息數(shù)量 static final Semaphore full = new Semaphore(0); //可重入鎖和條件變量 static Lock lock = new ReentrantLock(); static Condition condition = lock.newCondition(); //用與輔助的簡(jiǎn)單的生成消息 static Integer count = 0; //生產(chǎn)者 static class Producer extends Thread { Producer(String name) { super.setName(name); } @Override public void run() { do { try { Swait(this.getName(), mutex, empty); log.info("生產(chǎn)了一條消息:【{}】", count); buffer.add(count++); Thread.sleep(1000); Ssignal(this.getName(), mutex, full); } catch (InterruptedException e) { log.error("生產(chǎn)消息時(shí)產(chǎn)生異常!"); } } while (true); } } //消費(fèi)者 static class Consumer extends Thread { Consumer(String name) { super.setName(name); } @Override public void run() { do { try { Swait(this.getName(), mutex, full); log.info("消費(fèi)了一條消息:【{}】", buffer.remove(0)); Thread.sleep(1000); Ssignal(this.getName(), mutex, empty); } catch (InterruptedException e) { log.error("消費(fèi)消息時(shí)產(chǎn)生異常!"); } } while (true); } } public static void Swait(String id, Semaphore... list) throws InterruptedException { lock.lock(); //如果資源不足,就掛起線程,并將線程插入到condition的隊(duì)列中 while (true) { int count=0; for (Semaphore semaphore:list){ if(semaphore.availablePermits()>0){ count++; } } if(count == list.length){ break; } log.info("線程【{}】被掛起", id); condition.await(); log.info("被掛起的線程【{}】被喚醒執(zhí)行。", id); } log.info("為線程【{}】分配資源!", id); for (Semaphore semaphore:list){ semaphore.acquire(); } lock.unlock(); } public static void Ssignal(String id, Semaphore... list) throws InterruptedException { log.info("線程【{}】執(zhí)行了釋放資源", id); lock.tryLock(1, TimeUnit.SECONDS); for (Semaphore semaphore:list){ semaphore.release(); } //喚醒等待隊(duì)列中的一個(gè)線程 condition.signal(); lock.unlock(); } public static void main(String[] args) { Producer p1 = new Producer("p1"); Consumer c1 = new Consumer("c1"); p1.start(); c1.start(); }
上面代碼都是可以直接執(zhí)行的,如果不需要使用參數(shù)列表,可以將上面的Swait方法進(jìn)行替換即可(記得創(chuàng)建對(duì)應(yīng)的條件變量)。
到此,關(guān)于“JUC怎么模擬AND型信號(hào)量”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。