非阻塞式集合(Nom-Blocking Collection) ConcurrentLikedDeque
这类集合也包括添加和溢出数据的方法。如果方法不能立即被执行,则返回null或者抛出异常,但是调用这个方法的线程不会阻塞
阻塞式集合(Blocking Collection) LikeBlockingDeque
当即和已满或者为空时,被调用的添加或者移除方法就不能立即执行,那么调用的这个方法的线程将被阻塞,知道该方法可以被执行
ConcurrentHashMap前需要了解HashMap的数据结构
HashMap=数组+链表:HashMap里面就是一个类似数据的小桶(长度为3的数组,[0]:key [1]:value [2]:指向下一个,如果是最后一个则[3]:null)
JDK1.8之前如果HashMap里面的数量是100万,那么如果要拿到其中一个,就很耗费性能,但是在JDK1.8(包含)之后有了红黑树的感念,当HashMap里面的数据达到分配HashMap内存的75%后会以3倍的容量进行扩展。
private static final int MAXIMUM_CAPACITY = 1 << 30; 最大容量2的30次幂
private static final int DEFAULT_CAPACITY = 16; 默认容量16
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 最大数组数量0x7fffffff(16进制)==2147483647(十进制)-8
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;默认并发级别
private static final float LOAD_FACTOR = 0.75f;扩展因子
static final int TREEIFY_THRESHOLD = 8;阈值,当小8时为链表,当大于等于8时为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;红黑树最小扩展
我们可以一下put方法中的putval会发现出现了一个Synchronized关键字。其中里面还有一个静态segment内部类,它的意思就是分割的意思,默认HashMap里面的每一个元素都是进行分割。
可以想一下,为什么使用Synchronized锁而不在使用segment进行加锁了呢?经过百度查看大佬们的博客后总结:JDK1.8(不包含)之前,用的是segment方式继承ReentrantLock,实现的原理是将集合进行分割开
来再进行加锁,粒度比较大。而1.8之后使用Synchronized所得是链表中node(指的是一个元素对象)所以锁的更细了,粒度变小了。那么出现并发争抢的可能性还高吗?(当然会出现但是几率十分小)当多个线程争
抢一个node对象的时候,其中一个线程再使用node对象,其他线程只是在进行自旋,如果自旋数量达到阈值会形成线程阻塞(最糟糕的状态)。但是ReentrantLock就不一样它是先getState(CAS)然后进行线程占有,
如果没有占有到线程,那么他会直接挂起等待下次再获取。当然没获取到的时候可以进行TryLock,但是如果尝试获取锁超时或者当前线程直接中断了怎么办?难道在put的时候直接抛出InterruptedException异常?
这样就尴尬了。说白了就是1.8的升级更合理,更优化。从原来的1/16线程使用segment对象变成了1/N线程使用当前node节点对象。当N<16的时候锁的粒度上远远小于segment,阻塞的情况要小很多很多。
ConcurrentLinkedDeque
public class ConllectionDemo1 { private static ConcurrentLinkedDeque<String> cld = new ConcurrentLinkedDeque<String>(); public static void main(String args[]) throws InterruptedException { Thread[] add = new Thread[100]; for (int i = 0; i < 100; i++) { add[i] = new Thread(()->{ for (int j = 0; j < 10000; j++) { cld.add("element"+j); } }); add[i].start(); add[i].join(); } System.out.println("ADDcld:"+cld.size()); for (int i = 0; i <100 ; i++) { Thread[] poll = new Thread[100]; poll[i] = new Thread(()->{ for (int j = 0; j <5000 ; j++) { cld.pollLast(); cld.pollFirst(); } }); poll[i].start(); poll[i].join(); } System.out.println("POllcld:"+cld.size()); } }
LinkedBlockingDeque
public class LinkBlockingQueueDome { private static LinkedBlockingDeque<String> list = new LinkedBlockingDeque<String>(3); public static void main(String args[]){ Thread[] threads = new Thread[3]; //开始往里面加 for (int i =0; i <3 ; i++) { threads[i] = new Thread(()->{ for (int j = 0; j <5 ; j++) { try { list.put(j+""); System.out.println(j+"里面的数量是::"+list.size()+"-----"); } catch (InterruptedException e) { e.printStackTrace(); } } }); threads[i].start(); } //开始从里面去 for (int i = 0; i <5 ; i++) { for (int j = 0; j <3 ; j++) { try { TimeUnit.SECONDS.sleep(1); String str = list.take(); System.out.println("取出来的list:"+(str)+"里面的数量是:"+list.size()); } catch (InterruptedException e) { e.printStackTrace(); } } } try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end:"+list.size()); } }
LinkedTransferQueue 生产-消费
PriorityBlockingQueue 优先级
原子操作熟悉CAS,但是需要避免ABA问题,所以我们使用AtomicStampedReference具有状态位的原子性类进行处理