2017-05-30 27 views
3

我有原來的同步代碼,CompletableFuture和鎖

Lock lock = getLock(userId); // get a lock from guava Striped locks 
lock.lock(); 

try { 
    // do some database writing action 
    updateUserByUserId(userId); 
    return updateUserPropertiesByUserId(userId); 
} 
finally { 
    lock.unlock(); 
} 

鎖的目的是在一個更高的水平,以模仿悲觀數據庫鎖定。

在Java8中,引入了CompleteableFutures。 updateUserByUserId(userId);updateUserPropertiesByUserId(userId);現在可以返回CompletableFuture<Void>以完全異步執行。

我的問題是,我怎樣才能使用相同的機制?或者這種鎖定方式完全錯誤? (我真的不希望依賴於數據庫的鎖定。如果可能的話,我想在應用程序層而不是數據庫層來處理這個)

我已經試過

Lock lock = getLock(userId); // get a lock from guava Striped locks 

return CompletableFuture 
     .supplyAsync(() -> { 
      lock.lock(); 
     }) 
     .thenCompose(VOID -> updateUserByUserId(userId)) 
     .thenCompose(entity -> updateUserPropertiesByUserId(userId)) 
     .whenComplete((entity, ex) -> { 
      lock.unlock(); 
     }); 

但我可一直在IllegalMonitorStateExceptionlock.unlock();,這是預期的,因爲你不應該解鎖在另一個線程的鎖。

有什麼建議嗎?

+0

你的鎖在這裏模仿*悲觀*鎖定,不樂觀。我不明白爲什麼你不會依賴數據庫事務和鎖定。如果您的應用程序有多個實例正在運行,或者其他人正在訪問同一個數據庫,那麼您的鎖定機制將不起作用。 –

+0

你說的樂觀是對的。我已經改變它說悲觀。 這是一個簡單的例子來證明我正在嘗試做什麼。實際上,我的數據庫只有1個訪問器,這是我的微服務只有1個接口。在主動 - 主動設置中,將使用從緩存層檢索到的鎖。 爲什麼我試圖這樣做是因爲我想「快速失敗,提前失敗」而不是採取另一跳到數據庫,這將引發錯誤,讓應用層將數據庫錯誤轉換爲異常,然後趕上那個例外。這是一個很大的打擊。 – IsenGrim

+0

而數據庫錯誤轉換爲Java異常可能因數據庫不同而不同......甚至是同一數據庫的不同風格。如果可能,我真的不想處理。 – IsenGrim

回答

2

您可以爲回調的...Async variants提供一個執行器,因此將同一single threaded executor提供給鎖定和解鎖的一個回調應確保它們在同一個線程上執行。

Lock lock = getLock(userId); // get a lock from guava Striped locks 
Executor lockExecutor = Executors.newSingleThreadExecutor(); 

return CompletableFuture 
     .supplyAsync(() -> { 
      lock.lock(); 
     }, lockExecutor) 
     .thenCompose(VOID -> updateUserByUserId(userId)) 
     .thenCompose(entity -> updateUserPropertiesByUserId(userId)) 
     .whenCompleteAsync((entity, ex) -> { 
      lock.unlock(); 
     }, lockExecutor); 

注:我沒有運行它,希望它的作品!