繁体   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