我正在创建和使用同步数组列表,该列表正在被多个线程访问/修改。在某个操作中我想删除这个同步列表的所有元素并处理它们。
List<String> myList = Collections.synchronizedList(new ArrayList<>());
// adding to array
public void addElements(String s) {
myList.add(s)
}
public void removeAllAndProcess() {
Arrays.stream(myList.toArray()).forEach(s -> {
myList.remove(s);
process(s.toString());
});
}
// Some processing
public void process(String s) {
// if processing unsuccessful then again add to list : myList.add(s)
}
我想知道
toArray()
是否需要在同步块内?在调用 toArray
时并发添加到列表中是否会影响返回的数组?
Collections.synchronizedList
的 Javadoc 说:
用户在通过 Iterator、Spliterator 或 Stream 遍历返回列表时必须手动同步它
我不知道我们是否应该考虑那个处方适用于
List#toArray
。我会这么认为,但显然不是。
我去 OpenJDK 看源代码
ArrayList
here。显然ArrayList#toArray
实现是:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
Arrays.copyOf
的源代码最终调用System.arrayCopy
.
所以我们有一堆代码。而且它们都没有明确承诺线程安全。因此,虽然我无法直接回答您的问题,但我可以提供一个简单的替代解决方案。
AtomicReference
我会建议这个简单的解决方案作为替代方案:与其耗尽元素列表,不如替换整个列表,同时使用
AtomicReference
提供线程安全。
但是,我们需要使用这种方法在
addElement
方法上进行同步。一个线程可能会获得对列表的引用,第二个线程可能会用新列表替换列表并开始对旧列表进行迭代,而第一个线程会修改它仍然引用的旧列表。
虽然我们必须向
addElement
方法添加同步,但我们可以从列表本身中删除同步。在这种方法下,我们不再从列表中删除。我们对列表所做的唯一修改是通过现在同步的 addElement
方法添加。所以不需要打电话Collections.synchronizedList
.
对了,我把复数
addElements
改成了单数addElement
。
AtomicReference < List < String > > myListRef = new AtomicReference( new ArrayList<>() ) ;
// adding to array
public synchronized void addElement( String s ) {
myListRef.get().add( s ) ;
}
public void removeAllAndProcess() {
List < String > newList = new ArrayList<>() ;
List < String > oldList = myListRef.getAndSet( newList ) ;
oldList.forEach( ( String s ) -> {
myList.remove( s ) ;
process( s );
}
);
}
// Some processing
public void process(String s) {
boolean processedSuccessfully = … ;
if( ! processedSuccessfully ) {
myListRef.get().add( s ) ;
}
}
注意:我在这里的假设是您的
process
方法是only曾经从您的removeAllAndProcess
方法中调用过。
我对你的代码安排不满意。但是我忽略了这些问题,而是按照您的要求专注于您的问题。
我怀疑另一种数据结构,例如
Queue
会更适合您的目的。但是,我又一次忽略了这一点,而是按照您的要求专注于您的问题。
不需要额外的同步,因为
Collections.synchronizedList(xyz)
安全地包装了对底层xyz
的所有访问。
您可以通过在一个好的 IDE 中跟踪源代码来仔细检查您的 JDK 实现。在 OpenJDK20 中,您会看到
Collections.synchronizedList
返回一个 SynchronizedList
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
SynchronizedList
扩展了 SynchronizedCollection
实现了 toArray()
与对象同步以确保您有线程安全的操作:
public Object[] toArray() {
synchronized (mutex) {return c.toArray();}
}