Java 以线程安全的方式获取集合的内容
我想以多线程方式阅读java集合的内容。这里有很多相同背景的问题,但没有一个是关于具体阅读点的 我有一个整数集合。我只需要几个线程在上面迭代,每个线程一次提取一个整数。我希望确保所有集合都是迭代的,并且两个不同的线程不会两次提取整数 坦率地说,我不知道什么是有效的。我知道迭代器不是线程安全的,但当涉及到只读时,我不知道。我做了一些测试,试图找出线程错误,但没有达到100%的确定性:Java 以线程安全的方式获取集合的内容,java,multithreading,collections,Java,Multithreading,Collections,我想以多线程方式阅读java集合的内容。这里有很多相同背景的问题,但没有一个是关于具体阅读点的 我有一个整数集合。我只需要几个线程在上面迭代,每个线程一次提取一个整数。我希望确保所有集合都是迭代的,并且两个不同的线程不会两次提取整数 坦率地说,我不知道什么是有效的。我知道迭代器不是线程安全的,但当涉及到只读时,我不知道。我做了一些测试,试图找出线程错误,但没有达到100%的确定性: intimax=500; 集合li=新阵列列表(imax); 对于(int i=0;i
intimax=500;
集合li=新阵列列表(imax);
对于(int i=0;i
编辑:
在实际用例中,这个整数中的每一个都用于开始一项密集的工作,例如查找它是否为素数
上面的示例提取整数列表,没有重复或未命中,但我不知道这是否是偶然的
使用HashSet而不是ArrayList也可以,但这可能是偶然的
如果您有一个常规集合(不一定是列表),并且需要以多线程方式提取其内容,那么在实践中该怎么办?您可以使用
java.util.Collections
中提供的同步版本。或者您可以在java.util.concurrent
中尝试特殊的数据结构(例如ConcurrentHashMap
)
我更喜欢这两种,而不是我自己的
另一个想法是在必要时同步整个方法,而不仅仅是集合访问
记住,不可变对象总是线程安全的。您只需要同步共享的可变状态。这取决于集合。如果在读取过程中没有发生结构变化,那么您可以同时读取它,这很好。大多数集合不会更改只读或迭代的结构,因此这是可以的,但在执行此操作之前,请确保阅读您正在使用的集合的文档 例如: 请注意,此实现是不同步的。如果有多个线程 并发访问哈希集,并至少访问其中一个线程 修改集合时,它必须在外部同步 这意味着,只要不存在写操作,同时从两个线程读取就可以了
一种方法是分割数据,让每个线程读取
collection.size()/numberOfThreads
元素。
线程#我将从
collection.size()/numThreads*i
读取到collection.size()/numThreads*(i+1)
(注意:需要特别注意以确保最后一个元素不会丢失,可以通过将最后一个线程frpmcollection.size()/numThreads*i
设置为collection.size()
,但这可能会使最后一个线程做更多的工作,并使您等待挣扎的线程)
另一个选项是使用间隔的任务队列,每个线程将在队列不为空时读取元素,并在给定的间隔内读取元素。队列必须同步,因为它是由多个线程同时修改的。通常,通过迭代收集内容的成本不足以实现多线程。这是您在获取内容后对列表所做的操作。 所以你应该做的是:
请注意,在线程之间共享的所有对象都需要是线程安全的(例如,通过将所有访问包装在同步块中)。关于这一点的最佳信息来源是您的用例将受益于使用队列-有一些线程安全的实现,例如ArrayBlockingQueue
Collection<Integer> li = new ArrayList<Integer>(imax);
final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(li.size(), false, li);
Thread[] threads = new Thread[20];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread("Thread " + i) {
@Override
public void run() {
Integer i;
while ((i = queue.poll()) != null) {
System.out.println(i);
}
}
};
}
Collection li=newarraylist(imax);
final BlockingQueue=new ArrayBlockingQueue(li.size(),false,li);
线程[]线程=新线程[20];
对于(int i=0;i
这是线程安全的,每个线程都可以独立于初始集合中的其他线程工作。Ok。但请大家想想:如果我同步整个方法,我就失去了多线程的好处。@Jean-Yves:不太可能。我相信
ConcurrentHashMap
使用细粒度同步和/或允许同时进行多个读取。但这可能是错误的。但是,这种同步仍然有很多开销,如果您知道具体的实例,就可以避免这些开销