簡體   English   中英

同步與ReadWriteLock

[英]synchronized vs ReadWriteLock

嗨,我在以下代碼段中極大地簡化了Java問題

public class WhichJavaSynchroIsBestHere {

    private BlockingQueue<CustomObject> queue = new PriorityBlockingQueue<CustomObject>();

    public void add( CustomObject customO ) {

        // The custom objects do never have the same id
        // so it's no horizontal concurrency but more vertical one a la producer/consumer
        if ( !queue.contains( customO ) ) {
            // Between the two statement a remove can happen
            queue.add( customO );
        }
    }

    public void remove( CustomObject customO ) {
        queue.remove( customO );
    }

    public static class CustomObject {
        long id;
        @Override
        public boolean equals( Object obj ) {
            if ( obj == null || getClass() != obj.getClass() )
                return false;
            CustomObject other = (CustomObject) obj;
            return ( id == other.id;
        }
    }
}

因此,這更多的生產者/使用者問題,因為大概兩個調用add的線程不會傳遞相同的Customobject (id),但是如果一個線程正在調用add且對象與另一個線程調用remove的對象相同,則可能會發生這種情況。 在if條件和添加之間的代碼段在我看來不是線程,安全,我在考慮對象Lock(無synchronized塊)來保護該段,但是ReadWriteLock更好嗎?

這沒有什么區別,因為無論如何這兩個部分都需要寫鎖定。

ReadWriteLock的優點是可以輕松地允許可以使用共享訪問權限的多個Reader,並且與需要獨占訪問權限才能進行寫作的人員很好地合作。

您可以在“ contains代碼的周圍加上“讀取”鎖定,如果您將大量重復工作放在可能的工作中,那將是有道理的。 但是,如果比起工作的主要驅動因素(而不是測試的大部分時間都通過了),這更是一種針對極端情況的健全性檢查,那么在這種情況下就沒有理由進行讀取鎖定。 只需鎖定整個部分並完成即可。

在if條件和添加之間的代碼段對我來說似乎不是線程安全的

您是正確的,它不是線程安全的。 每當對一個對象進行多次調用(甚至是synchronized調用)時,如果多個線程訪問該對象,則需要擔心兩次調用之間對象的狀態會發生變化。 不僅僅是對象可能已被刪除,而且另一個生產者可能添加了重復的對象,從而導致隊列中出現重復。 比賽將是:

  1. 線程#1測試以查看對象A是否在隊列中,不是
  2. 線程#2測試以查看對象A是否在隊列中,不是
  3. 線程#1將對象A添加到隊列中
  4. 線程#2將對象A添加到隊列中

隊列中將有2個A副本。

我在考慮對象鎖(無同步塊)以保護該部分

如果您由於察覺到的性能問題而避開了synchronized ,那么請不要這樣做。 這是適合使用synchronized的最佳示例。 如果您在synchronized塊內執行所有操作,則可以擺脫BlockingQueue

ReadWriteLock更好嗎?

否,因為在兩種情況下,線程都是“寫入”隊列。 刪除將修改隊列與添加隊列一樣多。 如果沒有作者,但對寫線程具有獨占訪問權,則ReadWriteLock允許多個閱讀線程。 現在,對隊列的測試被視為已讀,但這不會為您節省很多,除非在很大的情況下,隊列中已經存在重復項。

另外,要非常注意queue.contains(customO) 大多數隊列(包括PriorityBlockingQueue )在隊列中的所有項目中運行,以查找您可能要添加的項目( O(N) )。 這可能非常昂貴,具體取決於集合中有多少個項目。

在我看來,這是使用ConcurrentSkipListSet的好地方。 您只需執行一個queue.add()即可which internally does a put-if-absent. You can do a which internally does a put-if-absent. You can do a queue.pollFirst()`刪除並獲取第一項。 然后,該集合將為您處理內存同步和鎖定,並解決爭用條件。

暫無
暫無

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

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