List/Map/Set/Queue

在Jdk早期版本,保证线程安全的思路就是将共享变量封装在了对象内部,控制访问路径

那么其早期的封装为:

SafeArrayList<T>{

//封装ArrayList

List<T> c = new ArrayList<>();

//控制访问路径

synchronized

T get(int idx){

return c.get(idx);

}

synchronized

void add(int idx, T t) {

c.add(idx, t);

}

synchronized

boolean addIfNotExist(T t){

if(!c.contains(t)) {

c.add(t);

return true;

}

return false;

}

}

将所有涉及并发的操作都进行了加锁

早期在Collections类中提供了各种线程安全的List和Map Set 就是如此实现的

但是上面需要注意到一点,存在竞态问题

而且对于容器的遍历,用迭代器进行遍历,遍历时候操作,不具有原子性,

所以在Collection提供的安全类中,会在遍历之前获取到this的锁,锁住自己的对象即可

后来在Jdk1.5之后,提供了性能更好的并发容器 图片

对于其中的List

提供了一个实现类 CopyOnWriteArrayList

就是通常大家说的写时复制集合

实现原理简单的概括为

在内部维护了一个数组,成员变量array指向这个内部数组,所有的读基于这个Array进行,在写的时候,将array复制一份

在新复制的数组上进行增加元素的操作,执行完成让array指向新的数组

那么其在使用过程中,要避免一些操作,比如首先是进行新插入的数据不能立刻被获取到,另外,其迭代器只是一个快照,不能进行增删改操作

Map

提供的实现类为ConcurrentHashMap和ConcurrentSkipListMap,区别在于ConcurrentHashMap的Key是无序的,而ConcurrentSkipListMap的key是有序的

两者都要求了key value不能为空,否则就会抛出NullPointException

图片

在ConcurrentSkipListMap中的SkipList本身就是一个数据结构,称为跳跃表,但是其插入 删除 新增 时间复杂度都是log n,所以在ConcurrentHashMap性能上不去的时候,可以使用ConcurrentSkipListMap

Set

实现类为CopyOnWriteArraySet 和 ConcurrentSkipListSet,

可以说基本和前面的CopyOnWriteArrayList和ConcurrentSkipListMap的原理是一致的

Queue

队列,在并发包中,Queue可以分为 阻塞和非阻塞

或者说 双端和单端,

单端只能单向入队,而双端可以双向入队 单端使用Queue,双端使用Deque

阻塞使用Blocking,可以细分为

单端阻塞队列: 实现可以有 ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue,LinkedTransferQueue,PriorityBlockingQueue

内部一般维护一个数组或者一个链表,甚至可以不维护队列,例如SynchronousQueue 生产者的入队操作必须等消费者消费了,LinkedBlockingQueue融合了LinkedBlockingQueue和SynchronousQueue的功能 性能比LinkedBlockingQueue更好

PriorityBlocking 支持按照优先级出队,DelayQueue支持延时出队

图片

双端阻塞队列:LinkedBlockingDeque

单端非阻塞队列:其实现为ConcurrentLinkedQueue

双端非阻塞队列:其实现为ConcurrentLinkedDeque

在使用同步容器中的队列汇总,都不要使用无界的队列,数据量过大的情况会导致OOM的错误出现

所以优先考虑支持有界的队列,比如 ArrayBlockingQueue, LinkedBlockingQueue支持有界的

发表评论

邮箱地址不会被公开。 必填项已用*标注