1.CopyOnWriteArraylist->vector
(1)支持多线程并发读取(get/遍历),只支持单线程写入(add,remove);
(2)当有修改操作发生时,会对原有的数组进行拷贝,拷贝出一个新的数组,修改操作在新的数组上发生,修改加锁。原有数组的查询操作不需要加锁保护,当修改线程执行完毕,再用新的数组替换原有的数组。
(3)把读写操作分离开,读不加锁,写加锁,用空间换取读不加锁。适合读多写少的场景。
2.ConcurrentHashMap->Hashtable
(1)1.7以前:分段锁
A.首先是一个segment的数组,每个segment的元素又是一个HashEntry数组,Node数组中又存放了链表。
B.锁定以segment为单位
C.初始化时,会创建所有segment数组的元素,而segment数组不能扩容,占用内存多
D.Put操作会锁住key对应的segment,元素放入HashEntry的数组+链表结构中,元素放入链表头
E.Get操作无锁,扩容会加锁
(2)1.8做了修改:
A.Hashtable是锁住了整个map集合,而ConcurrentHashMap,它只会锁住map集合中的一个桶,根据桶的多少,可以进一步提高并发度,只要读写操作落在不同的桶里,操作就可以并行执行
B.初始化数组时,懒惰初始化
C.当容量小于64首先尝试扩容,当超过这个容量并且链表大于8,会将链表树化,树化过程中会锁住链表头
D.Put操作会锁住链表头,新加的元素放入链表尾部
E.Get操作不需要加锁,仅需要用cas(乐观锁)保证元素的可见性
F.扩容以链表为单位扩容,当扩容时有多个线程来同时访问,这些线程会协助扩容
3.ConcurrentSkipListMap
数据结构:跳跃表
类似于之前LinkedHashMap,都可以保持元素遍历的顺序和放入的顺序是一致的
LinkedHashMap非线程安全,ConcurrentSkipLIstMap线程安全
4.BlockingQueue 阻塞队列
经常用来实现生产消费模式,来解耦生产者、消费者线程
```java
BlockingQueue<Item> queue = new LinkedBlockingDeque<>(5);
Thread t1 = new Thread(()-> {
try {
for (int i = 0; i < 6; i++) {
Item item = new Item("产品" + i);
queue.put(item);
System.out.println(Thread.currentThread().getName() + "生产了商品:" + item.name);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(()-> {
try{
for (int i = 0; i < 0; i++) {
Item item = queue.take();
System.out.println(Thread.currentThread().getName() + "消费了商品:" + item.name);
}
} catch (Exception e) {
}
});
```