番石榴条纹类的细粒度并发
這篇文章將介紹如何使用Guava中的Striped類來實(shí)現(xiàn)更細(xì)粒度的并發(fā)。 ConcurrentHashMap使用條帶化鎖定方法來增加并發(fā)性,并且Striped類通過賦予我們具有條帶化Locks , ReadWriteLocks和Semaphores的能力來擴(kuò)展此主體。 當(dāng)訪問對象或數(shù)據(jù)結(jié)構(gòu)(例如Array或HashMap)時,通常我們將在整個對象或數(shù)據(jù)結(jié)構(gòu)上進(jìn)行同步,但這是否總是必要的? 在某些情況下,答案是肯定的,但有時我們可能沒有達(dá)到這種程度的進(jìn)程鎖定,并且可以通過使用更精細(xì)的方法來提高應(yīng)用程序的性能。
條紋鎖定/信號量的用例
始終同步可能的最小部分代碼被視為最佳實(shí)踐。 換句話說,我們只想在更改共享數(shù)據(jù)的代碼部分之間進(jìn)行同步。 通過允許我們減少不同任務(wù)的同步,條帶化使這一步驟更進(jìn)一步。 例如,假設(shè)我們有一個表示遠(yuǎn)程資源的URL的ArrayList,并且我們想限制在任何給定時間訪問這些資源的線程總數(shù)。 限制對N個線程的訪問自然適合java.util.concurrent.Semaphore對象。 但是問題在于限制對我們ArrayList的訪問成為瓶頸。 這是Striped類提供幫助的地方。 我們真正想要做的是限制對資源的訪問,而不是對URL的容器的訪問。 假設(shè)每一個都是不同的,我們使用一種“條帶化”的方法,其中一次訪問每個 URL僅限于N個線程,從而提高了應(yīng)用程序的吞吐量和響應(yīng)能力。
創(chuàng)建條紋鎖/信號燈
要創(chuàng)建Striped實(shí)例,我們只需使用Striped類中可用的靜態(tài)因子方法之一即可。 例如,這是我們?nèi)绾蝿?chuàng)建帶區(qū)卷ReadWriteLock的實(shí)例:
Striped<ReadWriteLock> rwLockStripes = Striped.readWriteLock(10);在上面的示例中,我們創(chuàng)建了一個Striped實(shí)例,在該實(shí)例中急切創(chuàng)建了單獨(dú)的“條紋”,強(qiáng)引用。 影響是除非刪除Striped對象本身,否則不會垃圾收集鎖/信號燈。 但是,當(dāng)創(chuàng)建鎖/信號量時,Stranded類為我們提供了另一個選擇:
int permits = 5; int numberStripes = 10; Striped<Semaphore> lazyWeakSemaphore = Striped.lazyWeakSemaphore(numberStripes,permits);在此示例中, Semaphore實(shí)例將被延遲創(chuàng)建并包裝在WeakReferences因此可立即用于垃圾回收,除非有另一個對象掛在其上。
訪問/使用條紋鎖/信號燈
要使用之前創(chuàng)建的Striped ReadWriteLock ,請執(zhí)行以下操作:
String key = "taskA"; ReadWriteLock rwLock = rwLockStripes.get(key); try{rwLock.lock();..... }finally{rwLock.unLock(); }通過調(diào)用Striped.get方法,我們將返回一個與給定對象鍵相對應(yīng)的ReadWriteLock實(shí)例。 我們將在下面使用Striped類提供更多示例。
鎖/信號量檢索保證
當(dāng)檢索鎖/信號量實(shí)例時,Striped類保證objectA等于objectB(假設(shè)正確實(shí)現(xiàn)了equals和hashcode方法),對Striped.get方法的調(diào)用將返回相同的鎖/信號量實(shí)例。 但是不能保證相反的情況,也就是說,當(dāng)objectA不等于ObjectB時,我們?nèi)匀豢梢詸z索相同引用的鎖/信號量。 根據(jù)針對Striped類的Javadoc,指定更少的條紋會增加為兩個不同的鍵獲得相同引用的可能性。
一個例子
讓我們基于引言中介紹的場景來看一個非常簡單的示例。 我們構(gòu)造了一個ConcurrentWorker類,在其中我們希望限制對某些資源的并發(fā)訪問。 但讓我們假設(shè)有一些不同的資源,因此理想情況下,我們希望限制每個資源的訪問,而不僅僅是限制ConcurrentWorker 。
public class ConcurrentWorker {private Striped<Semaphore> stripedSemaphores = Striped.semaphore(10,3);private Semaphore semaphore = new Semaphore(3);public void stripedConcurrentAccess(String url) throws Exception{Semaphore stripedSemaphore = stripedSemaphores.get(url);stripedSemaphore.acquire();try{//Access restricted resource hereThread.sleep(25);}finally{stripedSemaphore.release();}}public void nonStripedConcurrentAccess(String url) throws Exception{semaphore.acquire();try{//Access restricted resource hereThread.sleep(25);}finally{semaphore.release();}} }在此示例中,我們有兩種方法ConcurrentWorker.stripedConcurrentAccess和ConcurrentWorker.nonStripedConcurrentAccess因此我們可以輕松進(jìn)行比較。 在這兩種情況下,我們都通過調(diào)用Thread.sleep 25毫秒來模擬工作。 我們創(chuàng)建了一個Semaphore實(shí)例和一個Striped實(shí)例,其中包含10個急切創(chuàng)建的,高度引用的Semaphore對象。 在這兩種情況下,我們都指定了3個許可,因此任何時候3個線程都可以訪問我們的“資源”。 這是一個簡單的驅(qū)動程序類,用于測量兩種方法之間的吞吐量。
public class StripedExampleDriver {private ExecutorService executorService = Executors.newCachedThreadPool();private int numberThreads = 300;private CountDownLatch startSignal = new CountDownLatch(1);private CountDownLatch endSignal = new CountDownLatch(numberThreads);private Stopwatch stopwatch = Stopwatch.createUnstarted();private ConcurrentWorker worker = new ConcurrentWorker();private static final boolean USE_STRIPES = true;private static final boolean NO_STRIPES = false;private static final int POSSIBLE_TASKS_PER_THREAD = 10;private List<String> data = Lists.newArrayList();public static void main(String[] args) throws Exception {StripedExampleDriver driver = new StripedExampleDriver();driver.createData();driver.runStripedExample();driver.reset();driver.runNonStripedExample();driver.shutdown();}private void runStripedExample() throws InterruptedException {runExample(worker, USE_STRIPES, "Striped work");}private void runNonStripedExample() throws InterruptedException {runExample(worker, NO_STRIPES, "Non-Striped work");}private void runExample(final ConcurrentWorker worker, final boolean isStriped, String type) throws InterruptedException {for (int i = 0; i < numberThreads; i++) {final String value = getValue(i % POSSIBLE_TASKS_PER_THREAD);executorService.submit(new Callable<Void>() {@Overridepublic Void call() throws Exception {startSignal.await();if (isStriped) {worker.stripedConcurrentAccess(value);} else {worker.nonStripedConcurrentAccess(value);}endSignal.countDown();return null;}});}stopwatch.start();startSignal.countDown();endSignal.await();stopwatch.stop();System.out.println("Time for" + type + " work [" + stopwatch.elapsed(TimeUnit.MILLISECONDS) + "] millis");}//details left out for clarity我們使用了300個線程,并分別兩次調(diào)用了這兩個方法,并使用StopWach類記錄了時間。
結(jié)果
如您所料,條帶化版本的性能要好得多。 這是其中一個測試運(yùn)行的控制臺輸出:
Time forStriped work work [261] millis Time forNon-Striped work work [2596] millis雖然Striped類并不適合所有情況,但當(dāng)您需要并發(fā)不同數(shù)據(jù)時,它確實(shí)很方便。 謝謝你的時間。
資源資源
- 番石榴API
- 實(shí)踐中的并發(fā)
- 這篇文章的源代碼
翻譯自: https://www.javacodegeeks.com/2013/09/fine-grained-concurrency-with-the-guava-striped-class.html
總結(jié)
以上是生活随笔為你收集整理的番石榴条纹类的细粒度并发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dnd是什么意思 dnd的解释
- 下一篇: 具有瞬态属性的视图对象的钝化和激活