011Java并发包006线程安全的Collection相关类

部分内容来自以下博客:

https://www.cnblogs.com/skywang12345/p/3498483.html

https://www.cnblogs.com/skywang12345/p/3498652.html

https://www.cnblogs.com/skywang12345/p/3503458.html

https://www.cnblogs.com/skywang12345/p/3498995.html

注意:本文基于JDK1.8进行记录。

1 分类

参照之前在学习集合时候的分类,可以将JUC下有关Collection相关的类进行分类。

CopyOnWriteArrayList:实现了List接口,相当于线程安全的ArrayList。

CopyOnWriteArraySet:继承于AbstractSet类,实现了Set接口,相当于线程安全的HashSet。CopyOnWriteArraySet内部包含一个CopyOnWriteArrayList对象,它是通过CopyOnWriteArrayList实现的。

ConcurrentSkipListSet:继承于AbstractSet类,实现了NavigableSet接口,相当于线程安全的TreeSet。它是通过ConcurrentSkipListMap实现的。

ConcurrentLinkedQueue:继承于AbstractQueue类,实现了Queue接口,是单向链表实现的线程安全的无界队列,该队列支持FIFO(先进先出)方式操作元素。

ConcurrentLinkedDeque:继承于AbstractCollection类,实现了Deque接口,是双向链表实现的线程安全的无界队列,该队列支持FIFO(先进先出)和FILO(先进后出)方式操作元素,相当于线程安全的LinkedList。

ArrayBlockingQueue:实现了BlockingQueue接口,是数组实现的线程安全的有界阻塞队列。

LinkedBlockingQueue:实现了BlockingQueue接口,是单向链表实现的线程安全的无界阻塞队列,该队列支持FIFO(先进先出)方式操作元素。

LinkedBlockingDeque:实现了BlockingDeque接口,是双向链表实现的线程安全的无界阻塞队列,该队列支持FIFO(先进先出)和FILO(先进后出)方式操作元素。

SynchronousQueue:实现了BlockingQueue接口,是一个不存储元素的阻塞队列,put操作必须等待take操作,否则不能添加元素并产生中断抛出异常。

DelayQueue:实现了BlockingQueue接口,是一个支持延迟获取的阻塞队列。

2 CopyOnWriteArrayList

2.1 说明

CopyOnWriteArrayList使用写时复制技术,其内部有个volatile修饰的数组用于保存数据,在操作数据时会创建新数组,并将更新后的数据拷贝到新数组中,最后再将该数组赋值给原数组,这就是它叫做CopyOnWriteArrayList的原因。

通过volatile关键字保证数据修改时的可见性,通过在操作数据前使用Lock互斥锁来保护数据。所以涉及到修改数据的操作,CopyOnWriteArrayList效率很低。

使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。迭代器支持hasNext()、next()等不可变操作,但不支持add()、remove()等可变操作。

2.2 构造方法

// 空参构造器,返回默认容量为0的集合。
public CopyOnWriteArrayList();
// 传入了一个数组的构造器。
public CopyOnWriteArrayList(E[] toCopyIn);
// 传入了一个集合的构造器。
public CopyOnWriteArrayList(Collection<? extends E> c);

2.3 常用方法

// 获取数组。
final Object[] getArray();
// 设置数组。
final void setArray(Object[] a);
// 获取个数。
public int size();
// 判断是否为空。
public boolean isEmpty();
// 判断是否包含指定数据。
public boolean contains(Object o);
// 计算指定数据首次出现的下标。
public int indexOf(Object o);
// 计算指定数据最后出现的下标。
public int lastIndexOf(Object o);
// 获取指定下标的元素。
public E get(int index);
// 设置指定下标的指定元素,并返回旧元素。
public E set(int index, E element);
// 添加元素,并返回是否成功。
public boolean add(E e);
// 在指定位置添加指定元素。
public void add(int index, E element);
// 删除元素,并返回是否成功。
public boolean remove(Object o);
// 删除指定位置的元素,并返回删除的元素。
public E remove(int index);

2.4 源码说明

2.4.1 获取元素

public E get(int index) {
    return get(getArray(), index);
}

private E get(Object[] a, int index) {
    return (E) a[index];
}

2.4.2 设置元素

public E set(int index, E element) {
    // 使用锁来保证线程安全。
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 获得array指向的引用地址。
        Object[] elements = getArray();
        // 获取指定位置的旧元素。
        E oldValue = get(elements, index);
        // 如果旧元素的引用和新元素的引用不同。
        if (oldValue != element) {
            // 创建新的数组并拷贝array数组的值,替换新数组指定位置的元素。
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len);
            newElements[index] = element;
            // 将array的引用地址指向新的数组
            setArray(newElements);
        } else {
            // 为了确保volatile的语义,任何一个读操作都应该是写操作的结构,当然这仅仅是语义的说明,去掉也是可以的。
            setArray(elements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

2.4.3 添加元素

public void add(int index, E element) {
    // 使用锁来保证线程安全。
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 获得array指向的引用地址。
        Object[] elements = getArray();
        int len = elements.length;
        // 如果指定位置越界,则抛出异常。
        if (index > len || index < 0)
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len);
        Object[] newElements;
        // 如果插入位置是末尾。
        int numMoved = len - index;
        if (numMoved == 0)
            // 将原数组进行拷贝并扩大一个容量。
            newElements = Arrays.copyOf(elements, len + 1);
        else {
            // 如果不是插入到末尾,则创建扩大一个容量的数组。
            newElements = new Object[len + 1];
            // 分段复制原数组,并空出指定位置。
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index, newElements, index + 1, numMoved);
        }
        // 设置指定位置的指定元素。
        newElements[index] = element;
        // 将array引用的地址指向新的数组。
        setArray(newElements);
    } finally {
        lock.unlock();
    }
}

2.4.4 删除元素

public E remove(int index) {
    // 使用锁来保证线程安全。
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 获得array指向的引用地址。
        Object[] elements = getArray();
        int len = elements.length;
        // 根据指定的位置获取元素。
        E oldValue = get(elements, index);
        // 如果指定的元素是最后一个元素。
        int numMoved = len - index - 1;
        if (numMoved == 0)
            // 将原数组进行拷贝截取并将array的引用地址指向新的数组。
            setArray(Arrays.copyOf(elements, len - 1));
        else {
            // 如果不是最后一个元素,则创建减少一个容量的数组。
            Object[] newElements = new Object[len - 1];
            // 分段复制原数组,并空出指定位置。
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index + 1, newElements, index, numMoved);
            // 将array的引用地址指向新的数组。
            setArray(newElements);
        }
        // 返回该位置上的元素。
        return oldValue;
    } finally {
        lock.unlock();
    }
}

3 ArrayBlockingQueue

3.1 说明

ArrayBlockingQueue内部是通过数组保存数据的,数组的容量是创建ArrayBlockingQueue时指定的,是有界的阻塞队列。

ArrayBlockingQueue与ReentrantLock是组合关系,ArrayBlockingQueue中包含一个ReentrantLock对象。

3.2 构造方法

// 指定长度的构造器,默认使用非公平锁。
public ArrayBlockingQueue(int capacity);
// 指定长度和锁的构造器,容量小于等于0会抛出异常。
public ArrayBlockingQueue(int capacity, boolean fair);
// 传入了一个集合,指定长度和锁的构造器。
public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c);

3.3 常用方法

// 获取个数。
public int size();
// 判断是否包含指定数据。
public boolean contains(Object o);
// 获取首元素,元素不存在会抛异常。
public E element();
// 添加指定元素作为尾元素,并返回是否成功,队列已满会抛异常。
public boolean add(E e);
// 删除首元素,并返回删除的元素,元素不存在会抛异常。
public E remove();
// 获取首元素,元素不存在会返回null。
public E peek();
// 添加指定元素作为尾元素,并返回是否成功。
public boolean offer(E e);
// 添加指定元素作为尾元素,并返回是否成功,队列已满会等待指定时间。
public boolean offer(E e, long timeout, TimeUnit unit);
// 删除首元素,并返回删除的元素,元素不存在会返回null。
public E poll();
// 删除首元素,并返回删除的元素,元素不存在会等待指定时间。
public E poll(long timeout, TimeUnit unit);
// 添加指定元素作为尾元素,队列已满会等待。
public void put(E e);
// 删除首元素,并返回删除的元素,元素不存在会等待。
public E take();

4 LinkedBlockingQueue

4.1 说明

LinkedBlockingQueue是一个单向链表实现的阻塞队列。该队列按FIFO(先进先出)排序元素,新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。

LinkedBlockingQueue是可选容量的(防止过度膨胀),即可以指定队列的容量。如果不指定,默认容量大小等于Integer.MAX_VALUE。

LinkedBlockingQueue在实现多线程对竞争资源的互斥访问时,对于插入和取出操作分别使用了不同的锁。此外,插入锁putLock和非满条件notFull相关联,取出锁takeLock和非空条件notEmpty相关联。通过notFull和notEmpty更细腻的控制锁。

4.2 属性

head是链表的表头。取出数据时,都是从表头head处插入。
last是链表的表尾。新增数据时,都是从表尾last处插入。
count是链表的实际大小,即当前链表中包含的节点个数。
capacity是列表的容量,它是在创建链表时指定的。
putLock是插入锁。
takeLock是取出锁。
notEmpty是非空条件。
notFull是非满条件。

4.3 构造方法

// 空参构造器,返回默认容量为整数最大值的集合。
public LinkedBlockingQueue();
// 指定长度的构造器,容量小于等于0会抛出异常。
public LinkedBlockingQueue(int capacity);
// 传入了一个集合的构造器。
public LinkedBlockingQueue(Collection<? extends E> c);

4.4 常用方法

同ArrayBlockingQueue。

5 SynchronousQueue

5.1 说明

SynchronousQueue是一个线程安全的队列,但是其内部不存储元素,添加一次后需要等待删除一次,删除一次后需要等待添加一次。

SynchronousQueue是经典的生产者消费者模式,支持公平锁和非公平锁,公平锁使用内部类TransferQueue,非公平锁使用内部类TransferStack。

5.2 构造方法

// 空参构造器,默认使用非公平锁。
public SynchronousQueue();
// 指定锁的构造器。
public SynchronousQueue(boolean fair);

5.3 常用方法

同ArrayBlockingQueue。

6 DelayQueue

6.1 说明

DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。

需要注意的是,DelayQueue不支持存储null元素。

6.2 构造方法

// 空参构造器。
public DelayQueue();
// 传入了一个集合的构造器。
public DelayQueue(Collection<? extends E> c);

6.3 常用方法

同ArrayBlockingQueue。

7 ConcurrentLinkedQueue

7.1 说明

ConcurrentLinkedQueue是一个基于链表的无界线程安全队列,它适用于高并发的场景,按照FIFO(先进先出)原则对元素进行排序。队列元素中不可以放置null元素(内部实现的特殊节点除外)。

ConcurrentLinkedQueue使用CAS来保证更新的线程安全,是一个非阻塞队列。

7.2 构造方法

// 空参构造器。
ConcurrentLinkedQueue();
// 传入了一个集合的构造器。
ConcurrentLinkedQueue(Collection<? extends E> c);

7.3 常用方法

// 获取个数。
public int size();
// 判断是否包含指定数据。
public boolean contains(Object o);
// 获取首元素,元素不存在会抛异常。
public E element();
// 添加指定元素作为尾元素,并返回是否成功,队列已满会抛异常。
public boolean add(E e);
// 删除首元素,并返回删除的元素,元素不存在会抛异常。
public E remove();
// 获取首元素,元素不存在会返回null。
public E peek();
// 添加指定元素作为尾元素,并返回是否成功。
public boolean offer(E e);
// 删除首元素,并返回删除的元素,元素不存在会返回null。
public E poll();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值