我想知道AtomicReferenceArray是否可以用作ConcurrentLinkedQueue的替代品(如果可以使用有界结构)。
我目前有类似的东西:
ConcurrentLinkedQueue<Object[]> queue = new ConcurrentLinkedQueue<Object[]>();
public void store(Price price, Instrument instrument, Object[] formats){
Object[] elements = {price, instrument, formats};
queue.offer( elements);
}
商店(..)由多个线程调用。
我还有一个消费者线程,它定期唤醒并消耗元素。
private class Consumer implements Runnable{
@Override
public void run(){
List<Object[]> holder = drain( queue );
for(Object[] elements : holder ){
for( Object e : elements ){
//process ...
}
}
private final List<Object[]> drain(){
//...
}
}
我可以更换ConcurrentLinkedQueue以支持AtomicReferenceArray并仍然保持线程安全方面吗?
具体来说,原子地存储元素并建立“之前发生”关系,以便消费者线程看到不同线程存储的所有元素?
我尝试阅读AtomicReferenceArray的源代码,但仍不完全确定。
干杯
答案 0 :(得分:1)
我想知道AtomicReferenceArray是否可以用作ConcurrentLinkedQueue的替代品(如果可以使用有界结构)。
基本上没有。
虽然对数组元素的单独更新是以原子方式进行的,但这对于队列来说还不够。您还需要跟踪队列的头部和尾部的位置,并且除非进行一些额外的锁定/同步,否则您无法通过队列单元读/写原子地执行此操作。
(这与AtomicReferenceArray
的内部工作无关。问题在于,API不会为您提供在单个原子操作中更新两件事所需的功能。)
答案 1 :(得分:1)
AtomicReferenceArray
可用作无锁单消费者/多生产者环缓冲区。几个月前,我experimenting有一个实现,并有一个工作原型。优点是减少垃圾创建,更好的缓存局部性,以及由于更简单而未满时的更好性能。缺点是当缓冲区已满时缺乏严格的fifo语义和性能差,因为生产者必须等待排放发生。这可以通过回退到ConcurrentLinkedQueue
以避免失速来缓解。
生产者必须看到发生前的边缘,以便他们获得一个独特的插槽。但是,由于只需要一个消费者,这可能会延迟到排水完成。在我的使用中,排放是跨线程摊销的,因此通过成功获取try-lock来选择消费者。该锁的释放提供了边缘,允许阵列更新在临界区内使用延迟集。
我只会在性能高度调整的专业场景中使用此方法。在我的使用中,它作为缓存的内部实现细节是有意义的。不过,我不会一般地使用它。