当前位置:   article > 正文

Java CopyOnWriteArrayList介绍和代码示例

copyonwritearraylist

特点

CopyOnWriteArrayList 是 Java 并发包中提供的一种线程安全的列表实现。它是通过在修改操作时创建底层数组的副本来实现线程安全的,因此被称为 “写时复制”(Copy-On-Write)。

下面是 CopyOnWriteArrayList 的一些特点和详细解释:

  1. 线程安全性:CopyOnWriteArrayList 是线程安全的,可以被多个线程同时访问而无需额外的同步措施。这是因为它的写操作是通过创建底层数组的副本来完成的,从而避免了并发修改的问题。

  2. 读操作无锁:读取操作不会阻塞其他读操作,因为每个读操作都是针对一个不变的副本进行的。这使得 CopyOnWriteArrayList 在读多写少的场景中具有较好的性能。

  3. 写操作复制数组:每当进行写操作(添加、修改或删除元素)时,CopyOnWriteArrayList 会创建一个底层数组的副本,并在副本上执行修改操作。这样可以确保在写操作期间,其他线程仍然可以安全地读取原始数组。

  4. 内存占用较高:由于每次写操作都会创建一个副本数组,CopyOnWriteArrayList 的内存占用较高。因此,在数据量较大或写操作频繁的场景下,使用 CopyOnWriteArrayList 可能会导致内存消耗过大。

  5. 适用场景:CopyOnWriteArrayList 适用于读多写少的场景,例如读取操作远远多于写入操作的数据缓存、事件订阅等。它提供了一种简单且线程安全的方式来处理这些场景。

代码示例

下面是一个简单示例,展示了如何使用 CopyOnWriteArrayList

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListExample {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

        // 添加元素
        list.add("Alice");
        list.add("Bob");
        list.add("Charlie");

        // 遍历元素
        for (String element : list) {
            System.out.println(element);
        }

        // 修改元素
        list.set(1, "David");

        // 删除元素
        list.remove(2);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在上述示例中,我们创建了一个 CopyOnWriteArrayList 对象 list,并进行了添加、遍历、修改和删除等操作。由于 CopyOnWriteArrayList 是线程安全的,我们无需额外的同步措施就可以进行这些操作。

需要注意的是,CopyOnWriteArrayList 的写操作相对较慢,因为它涉及复制底层数组,因此在写入操作较频繁的场景中,性能可能会受到影响。因此,根据实际情况选择合适的数据结构非常重要。

关于线程安全

下面是一个简单的示例代码,演示了 CopyOnWriteArrayList 的线程安全性:

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListExample {
    private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

    public static void main(String[] args) {
        // 创建并启动多个线程
        Thread thread1 = new Thread(new AddElementTask());
        Thread thread2 = new Thread(new RemoveElementTask());
        thread1.start();
        thread2.start();

        // 等待线程执行完毕
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印最终的列表内容
        System.out.println("Final List:");
        for (String element : list) {
            System.out.println(element);
        }
    }

    static class AddElementTask implements Runnable {
        @Override
        public void run() {
            for (int i = 1; i <= 5; i++) {
                list.add("Element " + i);
                System.out.println("Added Element " + i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class RemoveElementTask implements Runnable {
        @Override
        public void run() {
            for (int i = 1; i <= 3; i++) {
                if (list.size() > 0) {
                    String removedElement = list.remove(0);
                    System.out.println("Removed " + removedElement);
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

在上述示例中,我们创建了一个 CopyOnWriteArrayList 对象 list,并创建了两个线程,一个线程用于添加元素到列表中,另一个线程用于移除列表的元素。每个线程在执行操作之前都会休眠一段时间。

运行示例代码,可以看到多个线程并发地修改 CopyOnWriteArrayList,但不会出现线程安全问题。这是因为 CopyOnWriteArrayList 内部使用写时复制的机制,在写操作时会复制底层数组,并在复制的数组上进行修改,从而保证了线程安全性。

需要注意的是,虽然 CopyOnWriteArrayList 提供了线程安全性,但每次写操作都会创建一个底层数组的副本,因此在写操作频繁的场景中,性能可能会受到影响。因此,在选择数据结构时,需要根据实际需求权衡线程安全性和性能。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/249197
推荐阅读
相关标签
  

闽ICP备14008679号