我现在正在学习java.util.concurrent。我在试着理解CopyOnWriteArrayList。
正如我所理解的,这个类看起来像ArrayList,但是线程是安全的。如果你有大量的阅读和较少的写作,这门课非常有用。
这是我的例子。我如何使用它(只是为了学习的目的)?
我能那样用吗?
package Concurrency;
import java.util.concurrent.*;
class Entry {
private static int count;
private final int index = count++;
public String toString() {
return String.format(
"index:%-3d thread:%-3d",
index,
Thread.currentThread().getId());
}
}
class Reader implements Runnable {
private CopyOnWriteArrayList<Entry> list;
Reader(CopyOnWriteArrayList<Entry> list) { this.list = list; }
public void run() {
try {
while(true) {
if(!list.isEmpty())
System.out.println("-out " + list.remove(0));
TimeUnit.MILLISECONDS.sleep(100);
}
} catch (InterruptedException e) {
return;
}
}
}
class Writer implements Runnable {
private CopyOnWriteArrayList<Entry> list;
Writer(CopyOnWriteArrayList<Entry> list) { this.list = list; }
public void run() {
try {
while(true) {
Entry tmp = new Entry();
System.out.println("+in " + tmp);
list.add(tmp);
TimeUnit.MILLISECONDS.sleep(10);
}
} catch (InterruptedException e) {
return;
}
}
}
public class FourtyOne {
static final int nThreads = 7;
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<Entry> list = new CopyOnWriteArrayList<>();
ExecutorService exec = Executors.newFixedThreadPool(nThreads);
exec.submit(new Writer(list));
for(int i = 0; i < nThreads; i++)
exec.submit(new Reader(list));
TimeUnit.SECONDS.sleep(1);
exec.shutdownNow();
}
}发布于 2015-02-23 19:54:31
请注意,在您的例子中,您的一个作者的写作速度是给定读者的10倍,造成了大量的复制。还请注意,您的阅读器也在对列表执行写操作(remove())。
在这种情况下,您以惊人的速度写入列表,这会导致严重的性能问题,因为每次更新此列表时都会使用大量内存。
只有当同步开销存在问题且读取与结构修改的比率很高时,才会使用CopyOnWriteArrayList。当一个或多个读取器试图同时访问列表时,整个数组副本的成本将通过性能提高来摊销。这与传统的同步列表形成对比,在这种情况下,每个访问(读或写)都控制在某个互斥项下,因此只有一个线程可以同时对列表执行某些操作。
如果需要一个简单的线程安全列表,请考虑Collections.synchronizedList()提供的同步列表。
还请注意:
if(!list.isEmpty()){
System.out.println("-out " + list.remove(0));
}不是有效的编程,因为在if语句计算后,无法保证列表将为而不是为空。为了保证一致的效果,您需要直接检查list.remove()的返回值,或者将整个段包装在一个synchronized块中(而不是使用线程安全结构的目的)。
作为结构修改调用的remove()调用也应该被替换为一种类似于get()的方法,以确保在读取数据时不进行结构修改。
总之,我认为CopyOnWriteArrayList只需要以一种非常具体的方式使用,而且只有在传统的同步变得令人无法接受的缓慢时才能使用。虽然您的示例在您自己的计算机上运行得很好,但是扩展访问的规模会更大,并且会导致gc做太多的工作来维护堆空间。
https://stackoverflow.com/questions/28681765
复制相似问题