多线程篇(其它容器- CopyOnWriteArraySet)(持续更新迭代)

 一、简介

CopyOnWriteArraySet是线程安全的Set集合,相当于线程安全的HashSet。

注意:HashSet的实现是通过散列表HashMap实现的,但是CopyOnWriteArraySet是通过动态数组

CopyOnWriteArrayList实现的

二、特性

  • 适用于数据量较小且读多写少的场景
  • 它是线程安全的
  • 新增和删除等修改数据的操作开销很大,涉及到数组复制后续源码解析会详解
  • 迭代器只支持读取不支持变更,并且在迭代过程中其他线程写入集合不会发生并发冲突
  • 迭代器的数据来源于迭代时的快照数据

三、数据结构

CopyOnWriteArraySet的底层数据结构是CopyOnWriteArrayList实现的线程安全的可变数组

public class CopyOnWriteArraySet<E> extends AbstractSet<E>implements java.io.Serializable {
    private static final long serialVersionUID = 5457747651344034263L;

    private final CopyOnWriteArrayList<E> al;

}

四、部分源码解析

1. 构造方法

  • 无参构造方法,构造一个空的CopyOnWriteArrayList
public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }
  • 通过Collection构造
public CopyOnWriteArraySet(Collection<? extends E> c) {
		// 如果是CopyOnWriteArraySet类型,则获取到底层的CopyOnWriteArrayList对象创建新的CopyOnWriteArrayList对象
        if (c.getClass() == CopyOnWriteArraySet.class) {
            @SuppressWarnings("unchecked") CopyOnWriteArraySet<E> cc =
                (CopyOnWriteArraySet<E>)c;
            al = new CopyOnWriteArrayList<E>(cc.al);
        }
        else {
            al = new CopyOnWriteArrayList<E>();
            al.addAllAbsent(c);
        }
    }

2. 变更数据方法

  • add方法,直接调用CopyOnWriteArrayList的addIfAbsent方法实现不重复的插入数据
public boolean add(E e) {
        return al.addIfAbsent(e);
    }
  • remove方法,直接调用CopyOnWriteArrayList的remove方法实现删除数据
public boolean remove(Object o) {
        return al.remove(o);
    }

CopyOnWriteArraySet变更数据总结:

CopyOnWriteArraySet完全依赖于内部的CopyOnWriteArrayList的方法来实现,逻辑也很简单,

具体逻辑可以参考上一篇的CopyOnWriteArrayList的文章

3. 迭代器方法

  • iterator方法,同样也是直接调用CopyOnWriteArrayList的迭代器方法
public Iterator<E> iterator() {
        return al.iterator();
    }

四、案例

案例一:对比CopyOnWriteArraySet和HashSet

  • 公共类
private static void printAll(Set<String> set) {
        System.out.println(set);
    }

    static class SetRunnable implements Runnable {

        private final Set<String> set;

        SetRunnable(Set<String> set) {
            this.set = set;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                set.add(Thread.currentThread().getName() + "-" + i);
                printAll(set);
            }
        }
    }
  • 多线程操作HashSet
    @Test
    public void notUseJuc() throws InterruptedException {
        Set<String> set = new HashSet<>();
        final Thread th1 = new Thread(new SetRunnable(set));
        final Thread th2 = new Thread(new SetRunnable(set));

        th1.start();
        th2.start();
        th2.join();
    }

执行过程中有概率报错:

D:\mysoft\jdk-8u271\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\lib\idea_rt.jar=56700:D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\lib\idea_rt.jar;D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\plugins\junit\lib\junit5-rt.jar;D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\plugins\junit\lib\junit-rt.jar;D:\mysoft\jdk-8u271\jre\lib\charsets.jar;D:\mysoft\jdk-8u271\jre\lib\deploy.jar;D:\mysoft\jdk-8u271\jre\lib\ext\access-bridge-64.jar;D:\mysoft\jdk-8u271\jre\lib\ext\cldrdata.jar;D:\mysoft\jdk-8u271\jre\lib\ext\dnsns.jar;D:\mysoft\jdk-8u271\jre\lib\ext\jaccess.jar;D:\mysoft\jdk-8u271\jre\lib\ext\jfxrt.jar;D:\mysoft\jdk-8u271\jre\lib\ext\localedata.jar;D:\mysoft\jdk-8u271\jre\lib\ext\nashorn.jar;D:\mysoft\jdk-8u271\jre\lib\ext\sunec.jar;D:\mysoft\jdk-8u271\jre\lib\ext\sunjce_provider.jar;D:\mysoft\jdk-8u271\jre\lib\ext\sunmscapi.jar;D:\mysoft\jdk-8u271\jre\lib\ext\sunpkcs11.jar;D:\mysoft\jdk-8u271\jre\lib\ext\zipfs.jar;D:\mysoft\jdk-8u271\jre\lib\javaws.jar;D:\mysoft\jdk-8u271\jre\lib\jce.jar;D:\mysoft\jdk-8u271\jre\lib\jfr.jar;D:\mysoft\jdk-8u271\jre\lib\jfxswt.jar;D:\mysoft\jdk-8u271\jre\lib\jsse.jar;D:\mysoft\jdk-8u271\jre\lib\management-agent.jar;D:\mysoft\jdk-8u271\jre\lib\plugin.jar;D:\mysoft\jdk-8u271\jre\lib\resources.jar;D:\mysoft\jdk-8u271\jre\lib\rt.jar;D:\workspace\myself\mycsdn-demo\target\test-classes;D:\mavenRepository\junit\junit\4.13.2\junit-4.13.2.jar;D:\mavenRepository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.bufan.juc.JucSetTest,notUseJuc
[Thread-0-0, Thread-1-0]
[Thread-1-1, Thread-0-0, Thread-1-0]
[Thread-1-1, Thread-1-2, Thread-0-0, Thread-1-0]
[Thread-1-3, Thread-1-1, Thread-1-2, Thread-0-0, Thread-1-0]
[Thread-1-3, Thread-1-4, Thread-1-1, Thread-1-2, Thread-0-0, Thread-1-0]
[Thread-1-5, Thread-1-3, Thread-1-4, Thread-1-1, Thread-1-2, Thread-0-0, Thread-1-0]
[Thread-1-5, Thread-1-6, Thread-1-3, Thread-1-4, Thread-1-1, Thread-1-2, Thread-0-0, Thread-1-0]
[Thread-1-7, Thread-1-5, Thread-1-6, Thread-1-3, Thread-1-4, Thread-1-1, Thread-1-2, Thread-0-0, Thread-1-0]
[Thread-1-7, Thread-1-8, Thread-1-5, Thread-1-6, Thread-1-3, Thread-1-4, Thread-1-1, Thread-1-2, Thread-0-0, Thread-1-0]
[Thread-1-9, Thread-1-7, Thread-1-8, Thread-1-5, Thread-1-6, Thread-1-3, Thread-1-4, Thread-1-1, Thread-1-2, Thread-0-0, Thread-1-0]
Exception in thread "Thread-0" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
	at java.util.HashMap$KeyIterator.next(HashMap.java:1469)
	at java.util.AbstractCollection.toString(AbstractCollection.java:461)
	at java.lang.String.valueOf(String.java:2994)
	at java.io.PrintStream.println(PrintStream.java:821)
	at com.bufan.juc.JucSetTest.printAll(JucSetTest.java:40)
	at com.bufan.juc.JucSetTest.access$000(JucSetTest.java:15)
	at com.bufan.juc.JucSetTest$SetRunnable.run(JucSetTest.java:55)
	at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0
  • 多线程操作CopyOnWriteArraySet
    @Test
    public void useJuc() throws InterruptedException {
        Set<String> list = new CopyOnWriteArraySet<>();
        final Thread th1 = new Thread(new SetRunnable(list));
        final Thread th2 = new Thread(new SetRunnable(list));

        th1.start();
        th2.start();
        th2.join();
    }

执行过程不会报并发异常

D:\mysoft\jdk-8u271\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\lib\idea_rt.jar=56769:D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\lib\idea_rt.jar;D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\plugins\junit\lib\junit5-rt.jar;D:\mysoft\JetBrains\IntelliJ IDEA 2022.2.3\plugins\junit\lib\junit-rt.jar;D:\mysoft\jdk-8u271\jre\lib\charsets.jar;D:\mysoft\jdk-8u271\jre\lib\deploy.jar;D:\mysoft\jdk-8u271\jre\lib\ext\access-bridge-64.jar;D:\mysoft\jdk-8u271\jre\lib\ext\cldrdata.jar;D:\mysoft\jdk-8u271\jre\lib\ext\dnsns.jar;D:\mysoft\jdk-8u271\jre\lib\ext\jaccess.jar;D:\mysoft\jdk-8u271\jre\lib\ext\jfxrt.jar;D:\mysoft\jdk-8u271\jre\lib\ext\localedata.jar;D:\mysoft\jdk-8u271\jre\lib\ext\nashorn.jar;D:\mysoft\jdk-8u271\jre\lib\ext\sunec.jar;D:\mysoft\jdk-8u271\jre\lib\ext\sunjce_provider.jar;D:\mysoft\jdk-8u271\jre\lib\ext\sunmscapi.jar;D:\mysoft\jdk-8u271\jre\lib\ext\sunpkcs11.jar;D:\mysoft\jdk-8u271\jre\lib\ext\zipfs.jar;D:\mysoft\jdk-8u271\jre\lib\javaws.jar;D:\mysoft\jdk-8u271\jre\lib\jce.jar;D:\mysoft\jdk-8u271\jre\lib\jfr.jar;D:\mysoft\jdk-8u271\jre\lib\jfxswt.jar;D:\mysoft\jdk-8u271\jre\lib\jsse.jar;D:\mysoft\jdk-8u271\jre\lib\management-agent.jar;D:\mysoft\jdk-8u271\jre\lib\plugin.jar;D:\mysoft\jdk-8u271\jre\lib\resources.jar;D:\mysoft\jdk-8u271\jre\lib\rt.jar;D:\workspace\myself\mycsdn-demo\target\test-classes;D:\mavenRepository\junit\junit\4.13.2\junit-4.13.2.jar;D:\mavenRepository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.bufan.juc.JucSetTest,useJuc
[Thread-0-0, Thread-1-0]
[Thread-0-0, Thread-1-0]
[Thread-0-0, Thread-1-0, Thread-0-1]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5, Thread-1-5]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5, Thread-1-5, Thread-0-6]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5, Thread-1-5, Thread-0-6, Thread-1-6]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5, Thread-1-5, Thread-0-6, Thread-1-6, Thread-0-7]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5, Thread-1-5, Thread-0-6, Thread-1-6, Thread-0-7, Thread-1-7]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5, Thread-1-5, Thread-0-6, Thread-1-6, Thread-0-7, Thread-1-7, Thread-0-8]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5, Thread-1-5, Thread-0-6, Thread-1-6, Thread-0-7, Thread-1-7, Thread-0-8, Thread-1-8]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5, Thread-1-5, Thread-0-6, Thread-1-6, Thread-0-7, Thread-1-7, Thread-0-8, Thread-1-8, Thread-0-9]
[Thread-0-0, Thread-1-0, Thread-0-1, Thread-1-1, Thread-0-2, Thread-1-2, Thread-0-3, Thread-1-3, Thread-0-4, Thread-1-4, Thread-0-5, Thread-1-5, Thread-0-6, Thread-1-6, Thread-0-7, Thread-1-7, Thread-0-8, Thread-1-8, Thread-0-9, Thread-1-9]

Process finished with exit code 0

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wclass-zhengge

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值