Java并发教程–信号量
第一篇文章將介紹信號(hào)量-特別是對(duì)信號(hào)量進(jìn)行計(jì)數(shù) 。 信號(hào)量是用于限制對(duì)資源訪問(wèn)的經(jīng)常被誤解和使用不足的工具。 對(duì)于其他控制對(duì)資源的訪問(wèn)的方式,它們將被忽略。 但是信號(hào)量為我們提供了一個(gè)超越常規(guī)同步和其他工具所能提供的工具集的工具集。
那么什么是信號(hào)量? 想到信號(hào)量的最簡(jiǎn)單方法是將其視為允許n個(gè)單位被獲取并提供獲取和釋放機(jī)制的抽象。 它安全地允許我們確保在給定的時(shí)間只有n個(gè)進(jìn)程可以訪問(wèn)特定資源 。
一切都很好,但是這將達(dá)到什么目的呢? 好吧,這是一個(gè)示例,將有助于解釋其用法。 它使用位于1.5。中的java.util.concurrent包中精心設(shè)計(jì)的Semaphore類。
限制連接
也許我們有一個(gè)過(guò)程可以通過(guò)HTTP定期為我們下載資源。 我們不想向任何主機(jī)發(fā)送垃圾郵件,同時(shí),我們想限制正在建立的連接數(shù),因此我們不會(huì)耗盡允許的有限文件句柄或出站連接。 一種簡(jiǎn)單的方法是使用信號(hào)量:
public class ConnectionLimiter {private final Semaphore semaphore;private ConnectionLimiter(int maxConcurrentRequests) {semaphore = new Semaphore(maxConcurrentRequests);}public URLConnection acquire(URL url) throws InterruptedException,IOException {semaphore.acquire();return url.openConnection();}public void release(URLConnection conn) {try {/** ... clean up here*/} finally {semaphore.release();}} }對(duì)于資源有限的問(wèn)題,這是一個(gè)很好的解決方案。 對(duì)acquire()的調(diào)用將阻塞,直到獲得許可為止。 信號(hào)燈的優(yōu)點(diǎn)在于,它隱藏了管理訪問(wèn)控制,計(jì)算許可數(shù)以及確保正確的線程安全性的所有復(fù)雜性。
危險(xiǎn)性
與大多數(shù)鎖定或同步方法一樣,存在一些潛在問(wèn)題。
要記住的第一件事是, 始終釋放您獲得的東西 。 這是通過(guò)使用try..finally構(gòu)造完成的。
使用信號(hào)量時(shí),還有其他不太明顯的問(wèn)題可能會(huì)降臨您。 以下課程顯示了死鎖,只有您中最幸運(yùn)的人才能避免。 您會(huì)注意到,獲得兩個(gè)信號(hào)量許可的兩個(gè)線程的執(zhí)行順序相反。 (為簡(jiǎn)潔起見(jiàn),try..finally最終被省去了)。
public static void main(String[] args) throws Exception {Semaphore s1 = new Semaphore(1);Semaphore s2 = new Semaphore(1);Thread t = new Thread(new DoubleResourceGrabber(s1, s2));// now reverse them ... here comes trouble!Thread t2 = new Thread(new DoubleResourceGrabber(s2, s1));t.start();t2.start();t.join();t2.join();System.out.println("We got lucky!"); }private static class DoubleResourceGrabber implements Runnable {private Semaphore first;private Semaphore second;public DoubleResourceGrabber(Semaphore s1, Semaphore s2) {first = s1;second = s2;}public void run() {try {Thread t = Thread.currentThread();first.acquire();System.out.println(t + " acquired " + first);Thread.sleep(200); // demonstrate deadlocksecond.acquire();System.out.println(t + " acquired " + second);second.release();System.out.println(t + " released " + second);first.release();System.out.println(t + " released " + first);} catch (InterruptedException ex) {ex.printStackTrace();}} }如果運(yùn)行此程序,則很有可能會(huì)掛起一個(gè)進(jìn)程。 鎖排序的問(wèn)題與Java中的常規(guī)互斥鎖或同步一樣,也適用于信號(hào)量。 在某些情況下,超時(shí)(請(qǐng)參閱本文后面的tryAcquire()注釋)可用于防止死鎖導(dǎo)致進(jìn)程掛起,但是死鎖通常是可以避免的邏輯錯(cuò)誤的征兆。 如果您不熟悉死鎖,建議您仔細(xì)閱讀它們。 維基百科上有一篇關(guān)于死鎖的文章,該文章同樣適用于所有語(yǔ)言。
使用信號(hào)量(包括二進(jìn)制信號(hào)量,即互斥體)時(shí)應(yīng)注意的主要事項(xiàng)是:
- 獲取后不釋放(丟失的釋放調(diào)用或引發(fā)異常并且沒(méi)有finally塊)
- 長(zhǎng)時(shí)間保持信號(hào)量,導(dǎo)致線程饑餓
- 死鎖(如上所示)
有用的信號(hào)燈技巧
Java中Semaphores的一個(gè)有趣的特性是, 不必通過(guò)與Acquisition相同的線程來(lái)調(diào)用release 。 這意味著您可以具有一個(gè)線程限制器,該線程限制器可以通過(guò)調(diào)用acquire()來(lái)基于信號(hào)量池或創(chuàng)建線程。 然后,正在運(yùn)行的線程可以在完成時(shí)釋放其自己的信號(hào)燈許可。 這是Java中普通互斥鎖所沒(méi)有的有用屬性。
另一個(gè)技巧是在運(yùn)行時(shí)增加許可數(shù)量 。 與您可能會(huì)猜到的相反,信號(hào)量中的許可數(shù)量不是固定的,并且即使未進(jìn)行相應(yīng)的acquire()調(diào)用,對(duì)release()的調(diào)用也會(huì)始終增加許可的數(shù)量。 請(qǐng)注意,如果在沒(méi)有進(jìn)行acquire()的情況下錯(cuò)誤地調(diào)用release() ,這也會(huì)導(dǎo)致錯(cuò)誤。
最后,在Java的Semaphore中有一些有用的方法要熟悉。 方法AcquireInterruptible()將獲取資源,如果資源被中斷,則重新嘗試。 這意味著沒(méi)有對(duì)InterruptedException的外部處理。 tryAcquire()方法允許我們限制等待許可的時(shí)間-我們可以在沒(méi)有許可的情況下立即返回,也可以等待指定的超時(shí)時(shí)間。 如果您以某種方式知道了無(wú)法輕松修復(fù)或跟蹤的死鎖,則可以通過(guò)使用帶有適當(dāng)超時(shí)的tryAcquire()來(lái)幫助防止鎖定進(jìn)程。
用途
計(jì)數(shù)信號(hào)量有哪些可能的用途? 請(qǐng)注意以下幾點(diǎn):
- 限制對(duì)磁盤的并發(fā)訪問(wèn)(由于競(jìng)爭(zhēng)磁盤搜尋,這可能會(huì)降低性能)
- 線程創(chuàng)建限制
- JDBC連接池/限制
- 網(wǎng)絡(luò)連接限制
- 限制CPU或內(nèi)存密集型任務(wù)
當(dāng)然,信號(hào)量是訪問(wèn)控制和同步的一個(gè)很底層的構(gòu)建塊。 Java為我們提供了Java 1.5及更高版本中引入的大量并發(fā)機(jī)制和策略。 在接下來(lái)的文章中,我們將介紹一些更抽象的并發(fā)管理方法,包括執(zhí)行器,BlockingQueues,Barriers,Future以及一些新的并發(fā)Collection類。
您發(fā)現(xiàn)信號(hào)量有什么用途? 通過(guò)發(fā)表評(píng)論讓我們知道–我們喜歡語(yǔ)音軟件。
參考: Java并發(fā)第1部分–來(lái)自我們的JCG合作伙伴的信號(hào)燈 ,在Carfey Software博客上 。
相關(guān)文章 :- Java并發(fā)教程–重入鎖
- Java并發(fā)教程–線程池
- Java并發(fā)教程–可調(diào)用,將來(lái)
- Java并發(fā)教程–阻塞隊(duì)列
- Java并發(fā)教程– CountDownLatch
- Exchanger和無(wú)GC的Java
- Java Fork / Join進(jìn)行并行編程
- Java最佳實(shí)踐–隊(duì)列之戰(zhàn)和鏈接的ConcurrentHashMap
- 如何在不到1ms的延遲內(nèi)完成100K TPS
- 使用迭代器時(shí)如何避免ConcurrentModificationException
- 改善Java應(yīng)用程序性能的快速技巧
- 阻塞隊(duì)列示例以執(zhí)行命令
- 限制URL連接的信號(hào)量示例
- 執(zhí)行命令的同步隊(duì)列示例
- 更一般的等待/通知機(jī)制的CountDownLatch示例
翻譯自: https://www.javacodegeeks.com/2011/09/java-concurrency-tutorial-semaphores.html
總結(jié)
以上是生活随笔為你收集整理的Java并发教程–信号量的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java Micro-Benchmark
- 下一篇: 使用Java VisualVM分析您的应