synchronized与ReadWriteLock

时间:2016-10-20 21:35:58

标签: java multithreading concurrency locking

您好我已经极大地简化了以下代码片段中的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与第二个线程相同的对象时会发生这种情况呼叫删除。在if条件和添加之间的代码部分在我看来不是线程,安全,我正在考虑对象Lock(没有synchronized块)来保护该部分,但是{{1}更好?

2 个答案:

答案 0 :(得分:1)

这没有区别,因为无论如何这两个部分都需要写锁定。

ReadWriteLock的优点是可以轻松地允许多个可以使用共享访问的读者,并且与需要独占访问权限的人合作。

你可以使用读锁定来包围contains代码,如果你的大量工作中放入了潜在的重复数据,这将是有意义的。但是,如果对于一个罕见的边缘情况更加理智,而不是工作的主要驱动因素(即测试将在绝大部分时间内通过),那么在这种情况下没有理由进行读锁定。只需锁定整个部分即可完成。

答案 1 :(得分:0)

  

if条件和添加之间的代码部分在我看来不是线程安全的

你是对的,它不是线程安全的。无论何时对对象进行多次调用,甚至是synchronized对象,都需要担心如果多个线程访问,则调用之间的对象状态会发生变化。它不仅仅是一个对象可能已被删除,而是一个重复的对象可能由另一个生成器添加,导致队列中的重复。比赛将是:

  1. 线程#1测试以查看对象A是否在队列中,它不是
  2. 线程#2测试以查看对象A是否在队列中,它不是
  3. 线程#1将对象A添加到队列
  4. 线程#2将对象A添加到队列
  5. 队列中将有2个A副本。

      

    我正在考虑使用对象Lock(没有同步块)来保护该部分

    如果你因为感知到的表现问题而回避synchronized,那就不要了。这是使用synchronized的合适位置的完美示例。如果您在BlockingQueue块内进行所有操作,则可以删除synchronized

      

    是ReadWriteLock更好吗?

    不,因为在这两种情况下,线程都是&#34;写&#34;到队列。删除正在修改队列和添加一样多。如果没有编写者,则ReadWriteLock允许多个读取线程,但是对写入线程的独占访问权限。现在,队列的测试被认为是一个读取,但是除非有很多次存在队列中已有的重复项,否则不会为您节省很多。

    另外,要非常小心queue.contains(customO)。大多数队列(包括PriorityBlockingQueue)遍历队列中的所有项目以查找您可能正在添加的队列(O(N))。这可能非常昂贵,具体取决于集合中的项目数量。

    我觉得这是一个使用ConcurrentSkipListSet的好地方。你只需要执行queue.add()which internally does a put-if-absent. You can do a queue.pollFirst()`来删除并获取第一个项目。然后,该集合负责处理内存同步和锁定,并解决竞争条件。