簡體   English   中英

何時以及如何使用ConcurrentHashMap的其他同步?

[英]When and how should I use additional synchronization of ConcurrentHashMap?

我需要知道何時應該在使用ConcurrentHashMap時向我的代碼添加一些同步塊。 假設我有一個類似的方法:

private static final ConcurrentMap<String, MyObjectWrapper> myObjectsCache = new ConcurrentHashMap<>(CACHE_INITIAL_CAPACITY);

    public List<MyObject> aMethod(List<String> ids, boolean b) {
        List<MyObject> result = new ArrayList<>(ids.size());
        for (String id : ids) {
            if (id == null) {
                continue;
            }
            MyObjectWrapper myObjectWrapper = myObjectsCache.get(id);
            if (myObjectWrapper == null) {
                continue;
            }
            if (myObjectWrapper.getObject() instanceof MyObjectSub) {
                ((MyObjectSub) myObjectWrapper.getObject()).clearAField();
                myObjectWrapper.getObject().setTime(System.currentTimeMillis());
            }
            result.add(myObjectWrapper.getObject());
            if (b) {
                final MyObject obj = new MyObject(myObjectWrapper.getObject());
                addObjectToDb(obj);
            }
        }
        return result;
    }

我該如何有效地使這個方法並發? 我認為“get”是安全的但是一旦我從緩存中獲取值並更新緩存對象的字段 - 可能會出現問題,因為另一個線程可以獲得相同的包裝並嘗試更新相同的底層對象...我應該添加嗎同步? 如果是這樣,那么我應該從“get”到循環迭代結束還是整個循環同步?

也許有人可以分享一些更具體的指導方針,當需要對循環內的地圖鍵/值等進行更多操作時,正確有效地使用ConcurrentHashMap ...

我真的很感激。

編輯:問題的一些上下文:我正在研究生產代碼中的一些dao類的重構,並且一些類使用HashMaps來緩存從數據庫中檢索的數據。 使用緩存(用於寫入或讀取)的所有方法都將其整個內容放在同步(緩存)塊中(播放安全嗎?)。 我沒有太多的並發經驗,我真的想借此機會學習。 我天真地將HashMaps更改為ConcurrentHashMaps,現在想要刪除必要的同步bloocks。 所有緩存都用於寫入和讀取。 所提出的方法基於我已經改變的方法之一,現在我正在嘗試學習何時以及在何種程度上同步。 方法clearAField只更改包裝的POJO對象的一個​​字段的值,addObjectToDb嘗試將對象添加到數據庫。

其他示例是重新填充緩存:

public void findAll() throws SQLException{
    // get data from database into a list
    List<Data> data=getAllDataFromDatabase();
    cacheCHM.clear();
    cacheCHM.putAll(data);
} 

在這種情況下,我應該將clear和putAll放在一個synchronize(cacheCHM)塊中,對嗎?

我試圖找到並閱讀一些關於CHM的正確和有效使用的帖子/文章,但大多數涉及單一操作,沒有循環等......我發現的最好的是: http://www.javamadesoeasy .COM / 2015/04 / ConcurrentHashMap的功能於java.html

你沒有提到你期望在你的應用程序中發生什么並發,所以我假設你有多個線程調用aMethod沒有別的

你只需要一次調用ConcurrentHashMap: myObjectsCache.get(id) ,這很好。 事實上,因為沒有任何東西將數據寫入你的objectCache [見上面的假設]你甚至不需要ConcurrentHashMap! 你可以使用任何不可變的集合。 你最后有一個可疑的行: addObjectToDb(obj) ,這個方法是否也會影響你的緩存? 如果是這樣它仍然是安全的(可能,我們必須看到方法是確定的),但你肯定需要ConcurentHashMap。

危險在於您更改對象的位置,此處:

myObjectWrapper.getObject().clearAField();
myObjectWrapper.getObject().setTime(System.currentTimeMillis());

多個線程可以同時在同一個對象上調用這些方法。 不知道這些方法做了什么,我們不能說這是否安全。 如果這些方法都標記為同步,或者您注意確保這些方法同時運行是安全的,那么您就可以了(但請注意,這些方法的范圍可以按照您可能直觀預期的不同順序運行! )。 如果您不那么小心,那么可能存在數據損壞。

threadaftey和緩存的更好方法是使用不可變對象 如果它是不可變的,那么MyObjectSub calss可能會是什么樣子[不確定為什么需要包裝器 - 我完全可以省略它]:

//Provided by way of example. You should consider generating these 
//using http://immutables.github.io/ or similar
public class MyImmutableObject {
    //If all members are final primitives or immutable objects 
    //then this class is threadsafe.
    final String field;
    final long time;

    public MyImmutableObject(String field, long time) {
        this.field = field;
        this.time = time;
    }

    public MyImmutableObject clearField() {
        //Since final fields can never be changed, our only option is to 
        //return a copy.
        return new MyImmutableObject("", this.time);
    }

    public MyImmutableObject setTime(long newtime) {
        return new MyImmutableObject(this.field, newtime);
    }
}

如果您的對象是不可變的,那么線程安全性要簡單得多。 你的方法看起來像這樣:

public List<Result> typicialCacheUsage(String key) {
    MyImmutableObject obj = myObjectsCache.get(key);

    obj = obj.clearField();
    obj = obj.setTime(System.currentTimeMillis());

    //If you need to put the object back in the cache you can do this:
    myObjectsCache.put(key, obj);

    List<Result> res = generateResultFromObject(obj);
    return res;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM