使用ReentrantLock和Lambdas进行干净同步
最近,我在閱讀一篇內容豐富的文章,內容涉及Javin Paul 1 synchronized和ReentrantLock之間的區別。 他強調了后者的優點,但并未保留一些缺點,這些缺點與正確使用所需的繁瑣的try-finally塊有關。
在同意他的陳述的同時,我沉迷于一個想法,當它涉及到同步時,這總是困擾著我。 兩種方法混淆了單獨的關注點- 同步和同步內容的功能 -妨礙了逐一測試這些關注點。
作為探索性類型,我為過去已經嘗試過的該問題選擇了解決方案。 但是那時我不太喜歡編程模式。 這是由于由于匿名類而導致的冗長。 但是手頭有Java 8和Lambda表達式 ,我認為可能值得重新考慮。 因此,我復制了Javin Paul示例的“計數器”部分,編寫了一個簡單的測試用例,并開始進行重構。 這是最初的情況:
class Counter {private final Lock lock;private int count;Counter() {lock = new ReentrantLock();}int next() {lock.lock();try {return count++;} finally {lock.unlock();}} }可以清楚地看到丑陋的try-finally塊,它在實際功能2周圍產生了很多噪聲。 想法是將此塊移到其自己的類中,該類充當執行增量操作的一種同步方面。 下一個代碼片段顯示了這樣一個新創建的Operation接口的外觀,以及Lambda表達式3如何使用它:
class Counter {private final Lock lock;private int count;interface Operation<T> {T execute();}Counter() {lock = new ReentrantLock();}int next() {lock.lock();try {Operation<Integer> operation = () -> { return count++; };return operation.execute();} finally {lock.unlock();}} }在下面的類提取步驟中,引入了Synchronizer類型以用作執行程序,以確保在適當的同步范圍內執行給定的Operation :
class Counter {private final Synchronizer synchronizer;private int count;interface Operation<T> {T execute();}static class Synchronizer {private final Lock lock;Synchronizer() {lock = new ReentrantLock();}private int execute( Operation<Integer> operation ) {lock.lock();try {return operation.execute();} finally {lock.unlock();}}}Counter() {synchronizer = new Synchronizer();}int next() {return synchronizer.execute( () -> { return count++; } );} }如果我沒有完全弄錯的話,這應該和上課一樣。 好吧,測試是綠色的,但是普通的JUnit測試通常對并發沒有多大幫助。 但是,最后一次更改至少可以通過單元測試來驗證正確的調用順序,以確保同步:
public class Counter {final Synchronizer<Integer> synchronizer;final Operation<Integer> incrementer;private int count;public Counter( Synchronizer<Integer> synchronizer ) {this.synchronizer = synchronizer;this.incrementer = () -> { return count++; };}public int next() {return synchronizer.execute( incrementer );} }如您所見,“ Operation和“ Synchronizer已移至其自己的文件。 這樣,提供了同步方面,并且可以作為單獨的單元進行測試。 現在, Counter類使用構造函數注入同步器實例4 。 此外,增量操作已分配給名為“ incrementer”的字段。 為了簡化測試,最終字段的可見性已默認打開。 使用Mockito進行例如監視同步器的測試現在可以確保正確的同步調用如下:
@Test public void synchronization() {Synchronizer<Integer> synchronizer = spy( new Synchronizer<>() );Counter counter = new Counter( synchronizer );counter.next();verify( synchronizer ).execute( counter.incrementer );}通常,對于使用方法調用驗證,我不會太過退出,因為這會在單元和測試用例之間產生非常緊密的聯系。 但是鑒于上述情況,對我來說這并不是一個太糟糕的妥協。 但是,我只是使用Java 8和Lambda表達式進行第一次熱身,也許我在并發方面也缺少一些東西-那么您怎么看?
翻譯自: https://www.javacodegeeks.com/2014/04/clean-synchronization-using-reentrantlock-and-lambdas.html
總結
以上是生活随笔為你收集整理的使用ReentrantLock和Lambdas进行干净同步的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 欢迎使用Java 8之前要重温的10个J
- 下一篇: 使用Jenkins / Hudson远程