如果只有一个线程正在写入,则在 Java 中同步 List/Map

Synchronized List/Map in Java if only one thread is writing to it

第一个线程正在不断地用对象填充集合。第二个线程需要迭代这些对象,但它不会更改集合。

目前我使用 Collection.synchronized 使其成为线程安全的,但是有没有快速的方法来做到这一点?

更新

很简单:只要按下鼠标按钮,第一个线程(ui)就会不断地将鼠标位置写入ArrayList。第二个线程(渲染)根据列表绘制一条线。

如果您的阅读频率远高于写作频率,您可以使用 CopyOnWriteArrayList

即使您同步列表,在遍历它时也不一定是线程安全的,因此请确保同步它:

synchronized(synchronizedList) {
    for (Object o : synchronizedList) {
        doSomething()
    }
}

编辑:

这是一篇关于此事的非常清楚的文章: http://java67.blogspot.com/2014/12/how-to-synchronize-arraylist-in-java.html

如评论中所述,您需要在此列表上进行显式同步,因为迭代不是原子的:

List<?> list = // ...

线程 1:

synchronized(list) {
    list.add(o);   
}

线程 2:

synchronized(list) {
    for (Object o : list) {
        // do actions on object
    }
}

List 相比,Set 是否适合您的需求?

如果是这样,你可以使用Collections.newSetFromMap(new ConcurrentHashMap<>())

使用 java.util.concurrent.ArrayBlockingQueue.ArrayBlockingQueue BlockingQueue 的实现。它完全符合您的需求。

  1. 它非常适合生产者-消费者案例,因为这就是您的案例。

  2. 您还可以配置访问策略。 Javadoc 是这样解释访问策略的:

    Fair if true then queue accesses for threads blocked on insertion or removal, are processed in FIFO order; if false the access order is unspecified.

我目前可以想到 3 个选项来处理 ArrayList 中的并发性:-

  1. 正在使用 Collections.synchronizedList(列表) - 目前您正在使用它。

  2. CopyOnWriteArrayList - 行为很像 ArrayList class,不同之处在于当修改列表时,不是修改基础数组,而是创建一个新数组并丢弃旧数组。会比1慢。

  3. 正在使用 ReentrantReadWriteLock 创建自定义 ArrayList class。您可以围绕 ArrayList class 创建一个包装器。 reading/iterating/looping时使用读锁,在数组中添加元素时使用写锁。 例如:-

    import java.util.List;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class ReadWriteList<E> {
    
        private final List<E> list;
        private ReadWriteLock lock = new ReentrantReadWriteLock();
        private final Lock  r =lock.readLock();
        private final Lock  w =lock.writeLock();
    
        public ReadWriteList(List<E> list){
            this.list=list;
        }
    
        public boolean add(E e){
    
            w.lock();
            try{
                return list.add(e);
            }
            finally{
            w.unlock();
            }
        }
    
        //Do the same for other modification methods
    
        public E getElement(int index){
            r.lock();
            try{
    
                return list.get(index);
            }
            finally{
                r.unlock();
            }   
        }
    
        public List<E> getList(){
            r.lock();
            try{
            return list;
            }
            finally{
            r.unlock();
            }   
        }
        //Do the same for other read methods
    }