首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >同步vs ReadWriteLock

同步vs ReadWriteLock
EN

Stack Overflow用户
提问于 2016-10-21 05:35:58
回答 2查看 173关注 0票数 0

您好,我在下面的代码片段中极大地简化了一个java问题。

代码语言:javascript
复制
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条件和添加条件之间的代码部分在我看来不是线程,安全,我正在考虑使用对象锁(无synchronized块)来保护该部分,但是ReadWriteLock更好吗?

EN

回答 2

Stack Overflow用户

发布于 2016-10-21 05:41:51

这不会有什么不同,因为两个部分都需要写锁。

ReadWriteLock的优点是可以很容易地允许多个阅读器使用共享访问,而且还可以很好地与需要独占访问写入的人合作。

您可以使用读锁来包围contains代码,如果您的大部分工作都放入潜在的重复项,那么这将是有意义的。但是,如果它更多的是对罕见的边缘情况的理智检查,而不是您工作的主要驱动程序(即测试将通过绝大多数时间),那么在这种情况下就没有理由使用读锁定。只需锁定整个部分,并完成它。

票数 1
EN

Stack Overflow用户

发布于 2016-10-23 08:23:12

在我看来,

if条件和添加操作之间的代码部分不是线程安全的

你是对的,它不是线程安全的。任何时候你对一个对象有多个调用,即使是一个synchronized调用,你也需要担心如果被多个线程访问,对象的状态会在调用之间发生变化。这不仅仅是一个对象可能已经被删除,而是一个重复的对象可能是由不同的生产者添加的,从而在队列中造成重复。竞争将是:

  1. 线程#1测试对象A是否在队列中,不是
  2. 线程#2测试对象A是否在队列中,不是
  3. 线程#1将对象A添加到队列
  4. 线程#2将对象A添加到队列

<代码>G212

然后,队列中将有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 aqueue.pollFirst()`来移除并获取第一项。然后,该集合将为您处理内存同步和锁定,并解决争用条件。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40164956

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档