2015-09-25 81 views
1

我對使用Spring的競態條件有點擔心。 我知道所有種類的範圍之間(單,原型,會話等)的差異春季競賽條件單身服務有狀態或無狀態

我也知道:

  • 服務已經默認單範圍
  • 我不會有隻要我的豆是無狀態的,競賽條件的問題

即使我對這種無狀態的事情並不百分百確定。在討論有狀態時,我的研究來源始終只關注共享實例變量,但競爭條件不僅會在訪問實例變量時出現。 我創建了下面的例子來說明我的問題:

@Service 
public class AppleService { 

    @Autowired 
    private AppleRepository appleRepository; 

    @Override 
    public void doSomethingWithAppleCategory(String appleCategory) { 
    boolean existsAppleInCategory = existsAppleInCategory(appleCategory); 
    if(existsAppleInCategory) { 
     // do something 
    } 
    else { 
     throw new RuntimeException("There is no apple in the category: " + appleCategory); 
    } 
    } 

    private boolean existsAppleInCategory(String appleCategory) { 
    Iterable<Apple> allApples = appleRepository.findAll(); 
    return allApples.stream().anyMatch(a -> a.getAppleCategory().equals(appleCategory)); 
    } 
} 

你可以假設該服務在休息控制器或類似的東西使用。根據我的理解,當方法存在AppleInCategory被調用時,競爭條件可能會出現問題。例如,thread1具有其插槽並將「false」寫入變量existsAppleInCategory。然後,線程2使用「true」將其槽覆蓋existsAppleInCategory。之後線程1有另一個時隙。 =>線程1現在「做某事」而不是拋出一個RuntimeException。

我的假設是否正確?在這種情況下,我遇到競賽狀況問題嗎? 如果不是爲什麼?你能否推薦任何關於這個主題的資料(網上資源,書籍......)?

預先感謝您!

+0

不,你的理解是錯誤的......沒有共享狀態(假設'AppleRepository'也是一個線程安全的單例)。每個線程都有其自己的堆棧,並且在該堆棧上,其中包含方法內部使用的變量,所以thread1無法從thread2中看到「existsAppleInCategory」,反之亦然。如果'existsAppleInCategory'是一個實例級別的變量,這將是不同的! –

+0

當然你是對的。現在我覺得我是一個初學者:-) – user1713946

回答

1

假設AppleRepository是一個無狀態的單例,並在此數據庫中省略(因爲您的問題涉及到java)。

簡而言之,thread1無法看到thread2的變量,因此您沒有任何併發​​問題。

每個正在執行的線程都有自己的堆棧,在該堆棧上除了其他外還有方法中使用的變量,所以thread1無法從thread2看到existsAppleInCategory,反之亦然。該變量是方法的本地(實際上是執行塊),每個線程都有自己的副本。

如果existsAppleInCategory將是一個實例級別的變量,那麼這會有所不同,因爲您之後擁有共享狀態。在這種情況下,線程可能會看到每個其他數據(取決於何時寫入/讀取狀態以及使用關鍵字volatile)。

0

在你的例子中,你可以認爲appleRepository有狀態,因爲正如你在例子中描述的那樣,它可以改變。

因此,您必須考慮此類中的線程安全性(也許序列化?)。顯然,AppleService是線程安全的,因爲它是一個無狀態單例。您的競爭狀況問題將來自存儲庫,而不是來自單身人士。

一個很好的和基本的讀Java Tutorial Concurrency

0

是的,這裏是可以從數據庫級別的比賽條件。

如果查詢數據,然後插入或 同一事務中更新相關數據,定期SELECT語句不給予足夠的保護 。其他交易可以更新或刪除您剛剛查詢的相同行 。

因此,使用數據庫鎖定辦法使用

鎖定讀取(SELECT ... FOR UPDATE和SELECT ...LOCK IN SHARE MODE)

如果你想保護的Java代碼級使用synchronizationhttps://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

更多競爭條件:

MySQL的:http://dev.mysql.com/doc/refman/5.6/en/innodb-locking-reads.html

PLSQL:http://www.techonthenet.com/oracle/cursors/for_update.php

0

As M.Deinu m在其評論中說你的代碼不會出現任何競態條件問題,因爲existsAppleInCategory是一個函數的局部變量,而而不是的一個實例變量。每個線程都有自己的副本,一切都會好的。

這將是完全不同的

@Service 
public class AppleService { 

    @Autowired 
    private AppleRepository appleRepository; 

    private boolean _existsAppleInCategory; 

    @Override 
    public void productCallDetection(String appleCategory) { 
    _existsAppleInCategory = existsAppleInCategory(appleCategory); 
    if(existsAppleInCategory) { 
     // do something 
    } 
    ... 
    } 
    ... 
} 

,因爲現在你正在發生變化,便可獲得所有線程共享一個實例變量。