在Java中使用CopyOnWriteArrayList

时间:2015-02-23 19:28:29

标签: java concurrency

我正在研究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();
    }

}

2 个答案:

答案 0 :(得分:1)

请注意,在您的示例中,您的一位作家的写作速度是给定读者的10倍,导致制作了大量副本。另请注意,您的读者也在列表上执行写操作(remove())。

在这种情况下,您以极高的速率写入列表会导致严重的性能问题,因为每次更新此列表时都会使用大量内存。

CopyOnWriteArrayList仅在同步开销有问题且读取与结构修改的比率很高时使用。总阵列副本的成本由一个或多个读者尝试同时访问列表时的性能增益分摊。这与传统的同步列表形成对比,在传统的同步列表中,每个访问(读取或写入)都在某些互斥锁下进行控制,这样只有一个线程可以同时对列表执行某些操作。

如果需要简单的线程安全列表,请考虑Collections.synchronizedList()提供的同步列表。

请注意:

if(!list.isEmpty()){
    System.out.println("-out " + list.remove(0));
}

不是有效的编程,因为在if语句求值后,无法保证列表为空。为了保证一致的效果,您需要直接检查list.remove()的返回值或将整个段包装在synchronized块中(无法使用线程安全结构)。

作为结构修改调用的remove()调用也应该替换为get()之类的方法,以确保在读取数据时不进行任何结构修改。

总而言之,我认为CopyOnWriteArrayList只需要以非常特定的方式使用,并且只有当传统同步变得慢得令人无法接受时才需要。虽然您的示例可能在您自己的计算机上正常工作,但扩展访问的大小任何更大,并且您将导致gc做太多工作来维护堆空间。

答案 1 :(得分:0)

Java 1.5中引入了Java中的CopyOnWriteArrayList,它是Collections框架的一部分。

Java中的CopyOnWriteArrayList是线程安全的类。

Java中的

CopyOnWriteArrayList是ArrayList的线程安全类型,其中每个操作都是通过创建新的数组副本来实现的。

相关问题