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

最后都变了- 提交于 2019-12-24 04:06:47

问题


The first thread is filling a collection continuously with objects. A second thread needs to iterate over these objects, but it will not change the collection.

Currently I use Collection.synchronized for making it thread-safe, but is there a fast way to doing it?

Update

It's simple: The first thread (ui) continuously writes the mouse position to the ArrayList, as long as the mousebutton is pressed down. The second thread (render) draws a line based on the list.


回答1:


Even if you synchronize the list, it's not necessarily thread-safe while iterating over it, so make sure you synchronize on it:

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

Edit:

Here's a very clearly written article on the matter: http://java67.blogspot.com/2014/12/how-to-synchronize-arraylist-in-java.html




回答2:


Use java.util.concurrent.ArrayBlockingQueue.ArrayBlockingQueue implementation of BlockingQueue. It perfectly suits your needs.

  1. It is perfectly suited for producer-consumer cases as that is one in yours.

  2. You can also configure access policy. Javadoc explains access policy like this:

    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:


As mentioned in comments, you need explicit synchronization on this list, because iteration is not atomic:

List<?> list = // ...

Thread 1:

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

Thread 2:

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



回答4:


There are 3 options which I can currently think of to handle concurrency in ArrayList:-

  1. Using Collections.synchronizedList(list) - currently you are using it.

  2. CopyOnWriteArrayList - behaves much like ArrayList class, except that when the list is modified, instead of modifying the underlying array, a new array in created and the old array is discarded. It will be slower than 1.

  3. Creating custom ArrayList class using ReentrantReadWriteLock. You can create a wrapper around ArrayList class. Use read lock when reading/iterating/looping and use write lock when adding elements in array. For e.g:-

    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
    }
    



回答5:


If you're reading far more often than writing, you can use CopyOnWriteArrayList




回答6:


Rather than a List will a Set suit your needs?

If so, you can use Collections.newSetFromMap(new ConcurrentHashMap<>())



来源:https://stackoverflow.com/questions/32096419/synchronized-list-map-in-java-if-only-one-thread-is-writing-to-it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!