如果只有一个线程正在写入,则在 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 的实现。它完全符合您的需求。
它非常适合生产者-消费者案例,因为这就是您的案例。
您还可以配置访问策略。 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 中的并发性:-
正在使用 Collections.synchronizedList(列表) - 目前您正在使用它。
CopyOnWriteArrayList - 行为很像 ArrayList class,不同之处在于当修改列表时,不是修改基础数组,而是创建一个新数组并丢弃旧数组。会比1慢。
正在使用 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
}
第一个线程正在不断地用对象填充集合。第二个线程需要迭代这些对象,但它不会更改集合。
目前我使用 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 的实现。它完全符合您的需求。
它非常适合生产者-消费者案例,因为这就是您的案例。
您还可以配置访问策略。 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 中的并发性:-
正在使用 Collections.synchronizedList(列表) - 目前您正在使用它。
CopyOnWriteArrayList - 行为很像 ArrayList class,不同之处在于当修改列表时,不是修改基础数组,而是创建一个新数组并丢弃旧数组。会比1慢。
正在使用 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 }