首先我们先看下几个概念
高并发和多线程的区别? 多线程:是java的特性 高并发::不是java特有的东西,和语言无关同步和异步 同步:任务进行存在顺序(等待) 异步:多任务进行没有顺序(无需等待)并发和并行 并发:多个任务交替进行 并行:多个任务同时进行通过synchronized关键字修饰的容器,保证同一个时刻内只有一个线程在使用这个容器,从而使得容器线程安全
允许多线程同时使用容器,并且保证线程安全.而为了达到尽可能的提高并发,java并发工具包中采用了多种优化方式来提高并发容器的执行效率. 核心: 锁,cas,cow(读写分离),分段锁
Vector: 和Arraylist一样实现的List接口,区别在于Vector在可能出现线程不安全的所有方法都用了synchronized进行修饰 Stack: 是Vector的子类,Stock实现的是先进后出的栈,入栈,出栈都用了synchronized的关键字修饰 HashTable:实现Map接口,实现的功能和HashMap基本一致(HashTable不可能出现null,HashMap的键值可以存在null),HashTable也使用了synchronized修饰了方法
ArrayList,HashSet,HashMap这些都是线程不安全的,如何将这些线程不安全的转换成线程安全的呢? Collections提供了同步集合类
List<Object> objects = Collections.synchronizedList(new ArrayList<>()); Map<Object, Object> objectObjectMap = Collections.synchronizedMap(new HashMap<>()); Set<Object> objects1 = Collections.synchronizedSet(new HashSet<>()); 但是这种方式在多线程处理时效率是非常低的,在jdk1.5之后就提供的并发包,就可以解决多线下处理效率低的问题CopyOnWriteArrayList : 写时复制容器.写的是时候,会先复制一个容器 读: 从原来容器读 写: 把数据写到新的容器中 List.add()方法
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(); } }List.remove()方法
public E remove(int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) //如果是移除最后一个元素,拷贝(0,len-1)的集合对象 setArray(Arrays.copyOf(elements, len - 1)); else { Object[] newElements = new Object[len - 1]; //将原数组中从索引0开始拷贝index个元素,放到新中,从索引0开始 System.arraycopy(elements, 0, newElements, 0, index); //将原数组中从index+1号元素开始拷贝,放到新数组中,拷贝len - index - 1个原素 //两次加起来一共拷贝len-1个元素 System.arraycopy(elements, index + 1, newElements, index, numMoved); //替换原数组 setArray(newElements); } return oldValue; } finally { lock.unlock(); } }list.get()方法
public E get(int index) { //getArray()获取当前集合 return get(getArray(), index); } //获取集合对应索引的元素 private E get(Object[] a, int index) { return (E) a[index]; }以上是copyOnWriteArrayList的一些方法的源码,总结下来有两个缺点 1 占用系统资源,频繁创建对象 2 数据一致性问题: 只能保证最终一致性
ConcurrentHashMap : 在JDK1.7中:数组+链表+分段锁 在JDK1.8中:数组+链表+红黑树
1、当一个位置有多个元素的时候,ConcurrentHashMap优先采用链表的形式存储 2、如果链表的元素个数大于8个,并且数组的长度小于64,则扩容 3、如果链表的元素个数大于8个,并且数组的长度大于64,会将该节点的链表转换成红黑树
