您好,我在下面的代码片段中极大地简化了一个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条件和添加条件之间的代码部分在我看来不是线程,安全,我正在考虑使用对象锁(无synchronized块)来保护该部分,但是ReadWriteLock更好吗?
发布于 2016-10-21 05:41:51
这不会有什么不同,因为两个部分都需要写锁。
ReadWriteLock的优点是可以很容易地允许多个阅读器使用共享访问,而且还可以很好地与需要独占访问写入的人合作。
您可以使用读锁来包围contains代码,如果您的大部分工作都放入潜在的重复项,那么这将是有意义的。但是,如果它更多的是对罕见的边缘情况的理智检查,而不是您工作的主要驱动程序(即测试将通过绝大多数时间),那么在这种情况下就没有理由使用读锁定。只需锁定整个部分,并完成它。
发布于 2016-10-23 08:23:12
在我看来,
if条件和添加操作之间的代码部分不是线程安全的
你是对的,它不是线程安全的。任何时候你对一个对象有多个调用,即使是一个synchronized调用,你也需要担心如果被多个线程访问,对象的状态会在调用之间发生变化。这不仅仅是一个对象可能已经被删除,而是一个重复的对象可能是由不同的生产者添加的,从而在队列中造成重复。竞争将是:
<代码>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()`来移除并获取第一项。然后,该集合将为您处理内存同步和锁定,并解决争用条件。
https://stackoverflow.com/questions/40164956
复制相似问题