Java并发(四)并发容器

本文详细介绍了Java并发容器,包括CopyOnWriteArrayList和CopyOnWriteArraySet的实现原理和适用场景,分析了JDK1.7和1.8中ConcurrentHashMap的区别,并探讨了基于SkipList的ConcurrentSkipListMap。此外,还讨论了各种并发队列,如无锁非阻塞队列、阻塞队列和优先级队列的特性与应用场景。
摘要由CSDN通过智能技术生成

并发容器

写时拷贝技术的容器类

写时拷贝是解决并发问题的一种重要思路

有两个简单的类CopyOnWriteArrayList和CopyOnWriteArraySet

场景:
不可变对象的写操作:如String的replace方法,其并没有更改原字符串里面value[]数组的内容,而是创建了一个新字符串;
函数式编程:函数式编程的基础是不可变性(Immutability),所以函数式编程里面所有的修改操作都需要Copy-on-Write来解决,不像CopyOnWriteArrayList那样笨重,整个数组都复制一遍,而是可以进行按需复制;
linux:fork()子进程的时候,并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间;只用在父进程或者子进程需要写入的时候才会复制地址空间,从而使父子进程拥有各自的地址空间;而不会创建父进程的一个完整副本

1 CopyOnWriteArrayList

CopyOnWriteArrayList实现了List接口,它的用法与其他List如ArrayList基本是一样的,区别是:

  • 它是线程安全的,可以被多个线程并发访问
  • 它的迭代器不支持修改操作,但也不会抛出ConcurrentModificationException,即迭代时不需要加锁,解决了synchronized的迭代问题
public static void main(String[] args) {
   
    //final List<String> list = Collections.synchronizedList(new ArrayList<String>());改为
    final List<String> list = new CopyOnWriteArrayList<>();
    startIteratorThread(list);
    startModifyThread(list);
}
  • 它以原子方式支持一些复合操作,解决了synchronized的复合操作问题
//不存在才添加,如果添加了,返回true,否则返回false
public boolean addIfAbsent(E e)
//批量添加c中的非重复元素,不存在才添加,返回实际添加的个数
public int addAllAbsent(Collection<? extends E> c)
1.1 实现原理

CopyOnWriteArrayList的内部是一个以原子方式被整体更新的数组:

//volatile保证内存可见性:写操作更改了之后,读操作能看到
private transient volatile Object[] array;

写操作(写时拷贝的体现):每次修改操作,都会新建一个数组,复制原数组的内容到新数组,在新数组上进行需要的修改,然后以原子方式设置内部的数组引用

读操作:先拿到当前引用的数组,然后直接访问该数组,在读的过程中,可能内部的数组引用已经被修改了,但不会影响读操作,它依旧访问原数组内容

读不需要锁,可以并行,读和写也可以并行,但多个线程不能同时写,每个写操作都需要先获取锁,CopyOnWriteArrayList内部使用ReentrantLock:

transient final ReentrantLock lock = new ReentrantLock();

访问数组:

final Object[] getArray() {
   
    return array;
}

设置数组:

final void setArray(Object[] a) {
   
    array = a;
}

默认构造方法:

//设置了一个空数组
public CopyOnWriteArrayList() {
   
    setArray(new Object[0]);
} 

添加元素:

//为修改操作,整个过程需要被锁保护
//先拿到当前数组elements,然后复制了个长度加1的新数组newElements,在新数组中添加元素
//最后调用setArray原子性的修改内部数组引用
public boolean add(E e) {
   
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
   
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
   
        lock.unlock();
    }
}

查找元素:

public int indexOf(Object o) {
   
    Object[] elements = getArray();//拿到当前数组elements
    return indexOf(o, elements, 0, elements.length);
}
//所有数据都是通过参数传递进来的,数组内容也不会被修改,不存在并发问题
private static int indexOf(Object o, Object[] elements,int index, int fence) {
   
    if 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值