问题
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.
It is perfectly suited for producer-consumer cases as that is one in yours.
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:-
Using Collections.synchronizedList(list) - currently you are using it.
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.
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