番石榴条纹类的细粒度并发
這篇文章將介紹如何使用Guava中的Striped類來實現更細粒度的并發。 ConcurrentHashMap使用條帶化鎖定方法來增加并發性,并且Striped類通過賦予我們具有條帶化Locks , ReadWriteLocks和Semaphores的能力來擴展此主體。 當訪問對象或數據結構(例如Array或HashMap)時,通常我們將在整個對象或數據結構上進行同步,但這是否總是必要的? 在某些情況下,答案是肯定的,但有時我們可能沒有達到這種程度的進程鎖定,并且可以通過使用更精細的方法來提高應用程序的性能。
條紋鎖定/信號量的用例
始終同步可能的最小部分代碼被視為最佳實踐。 換句話說,我們只想在更改共享數據的代碼部分之間進行同步。 通過允許我們減少不同任務的同步,條帶化使這一步驟更進一步。 例如,假設我們有一個表示遠程資源的URL的ArrayList,并且我們想限制在任何給定時間訪問這些資源的線程總數。 限制對N個線程的訪問自然適合java.util.concurrent.Semaphore對象。 但是問題在于限制對我們ArrayList的訪問成為瓶頸。 這是Striped類提供幫助的地方。 我們真正想要做的是限制對資源的訪問,而不是對URL的容器的訪問。 假設每一個都是不同的,我們使用一種“條帶化”的方法,其中一次訪問每個 URL僅限于N個線程,從而提高了應用程序的吞吐量和響應能力。
創建條紋鎖/信號燈
要創建Striped實例,我們只需使用Striped類中可用的靜態因子方法之一即可。 例如,這是我們如何創建帶區卷ReadWriteLock的實例:
Striped<ReadWriteLock> rwLockStripes = Striped.readWriteLock(10);在上面的示例中,我們創建了一個Striped實例,在該實例中急切創建了單獨的“條紋”,強引用。 影響是除非刪除Striped對象本身,否則不會垃圾收集鎖/信號燈。 但是,當創建鎖/信號量時,Stranded類為我們提供了另一個選擇:
int permits = 5; int numberStripes = 10; Striped<Semaphore> lazyWeakSemaphore = Striped.lazyWeakSemaphore(numberStripes,permits);在此示例中, Semaphore實例將被延遲創建并包裝在WeakReferences因此可立即用于垃圾回收,除非有另一個對象掛在其上。
訪問/使用條紋鎖/信號燈
要使用之前創建的Striped ReadWriteLock ,請執行以下操作:
String key = "taskA"; ReadWriteLock rwLock = rwLockStripes.get(key); try{rwLock.lock();..... }finally{rwLock.unLock(); }通過調用Striped.get方法,我們將返回一個與給定對象鍵相對應的ReadWriteLock實例。 我們將在下面使用Striped類提供更多示例。
鎖/信號量檢索保證
當檢索鎖/信號量實例時,Striped類保證objectA等于objectB(假設正確實現了equals和hashcode方法),對Striped.get方法的調用將返回相同的鎖/信號量實例。 但是不能保證相反的情況,也就是說,當objectA不等于ObjectB時,我們仍然可以檢索相同引用的鎖/信號量。 根據針對Striped類的Javadoc,指定更少的條紋會增加為兩個不同的鍵獲得相同引用的可能性。
一個例子
讓我們基于引言中介紹的場景來看一個非常簡單的示例。 我們構造了一個ConcurrentWorker類,在其中我們希望限制對某些資源的并發訪問。 但讓我們假設有一些不同的資源,因此理想情況下,我們希望限制每個資源的訪問,而不僅僅是限制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因此我們可以輕松進行比較。 在這兩種情況下,我們都通過調用Thread.sleep 25毫秒來模擬工作。 我們創建了一個Semaphore實例和一個Striped實例,其中包含10個急切創建的,高度引用的Semaphore對象。 在這兩種情況下,我們都指定了3個許可,因此任何時候3個線程都可以訪問我們的“資源”。 這是一個簡單的驅動程序類,用于測量兩種方法之間的吞吐量。
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個線程,并分別兩次調用了這兩個方法,并使用StopWach類記錄了時間。
結果
如您所料,條帶化版本的性能要好得多。 這是其中一個測試運行的控制臺輸出:
Time forStriped work work [261] millis Time forNon-Striped work work [2596] millis雖然Striped類并不適合所有情況,但當您需要并發不同數據時,它確實很方便。 謝謝你的時間。
資源資源
- 番石榴API
- 實踐中的并發
- 這篇文章的源代碼
翻譯自: https://www.javacodegeeks.com/2013/09/fine-grained-concurrency-with-the-guava-striped-class.html
總結
以上是生活随笔為你收集整理的番石榴条纹类的细粒度并发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dnd是什么意思 dnd的解释
- 下一篇: 具有瞬态属性的视图对象的钝化和激活