1.CopyOnWriteArraylist->vector
(1)支持多线程并发读取(get/遍历),只支持单线程写入(add,remove);
(2)当有修改操作发生时,会对原有的数组进行拷贝,拷贝出一个新的数组,修改操作在新的数组上发生,修改加锁。原有数组的查询操作不需要加锁保护,当修改线程执行完毕,再用新的数组替换原有的数组。
(3)把读写操作分离开,读不加锁,写加锁,用空间换取读不加锁。适合读多写少的场景。
package com.yan.collection;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 1、支持多线程并发读取(get/遍历),只支持单线程写入(add,remove);
* 2、当有修改操作发生时,会对原有的数组进行拷贝,拷贝出一个新的数组,修改操作在新的数组上发生,修改加锁。
* 原有数组的查询操作不需要加锁保护,当修改线程执行完毕,再用新的数组替换原有的数组。
* 3、把读写操作分离开,读不加锁,写加锁,用空间换取读不加锁。适合读多写少的场景。
* @author 17912
*
*/
public class CopyOnWriteArrayListDemo implements Runnable {
//实现了List接口
//内部持有一个ReentrantLock lock = new ReentrantLock();
//底层是用volatile transient声明的数组 array
//读写分离,写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array
private CopyOnWriteArrayList<String> coalList = new CopyOnWriteArrayList<String>();
private List<String> list = new ArrayList<String>();
private static int i = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new Thread(new CopyOnWriteArrayListDemo()).start();
}
Thread.sleep(10000);
System.out.println(i+"");
}
@Override
public void run() {
coalList.add((i++)+"");
//list.add((i++)+"");
}
/**Vector是增删改查方法都加了synchronized,保证同步,但是每个方法执行的时候都要去获得锁,
* 性能就会大大下降,而CopyOnWriteArrayList 只是在增删改上加锁,但是读不加锁,
* 在读方面的性能就好于Vector,CopyOnWriteArrayList支持读多写少的并发情况。
*/
}
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.扩容以链表为单位扩容,当扩容时有多个线程来同时访问,这些线程会协助扩容。