java的concurrent包的存储类
简略的翻看一下concurrent包,一部分是通过继承AbstractQueuedSynchronizer实现(ReentrantLock、CountDownLatch、semaphore等),一部分通过lock实现(CycliBarrier、atomic、blockingqueue、concurrentMap等)。抽象类AbstractQueuedSynchronizer又主要是通过一个自定义的Node的自定义队列来存储线程,然后通过unsafe类(native)来实现指令级别的compare and swap获取对内存中的共享区域控制权(也就是锁)。然后unpark、park操作线程的入队、出队(在Node上解释的使用一个FIFO的CLH对列来存放)。现在描述下concurrent中的集合和atomic。
1、atomic
原子量:保证了CPU读、修改、写。但是不保证可见性,数据不知道是刷新到主存还是缓存。包括了:AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference、AtomicStamped、AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。
atomic变量都是使用
private volatile int value;
作为存储量,然后利用本地方法:unsafe中的compareAndSwapInt()等方法来修改内存中的数据,从而达到原子操作的功能。
2、blockingQueue
blockingQueue集合是concurrent最有用的工具集合之一。它运行线程从一边存放、一边读取。并且读为空、写满的情况都会阻塞。
这个接口主要有下面的方法
这个接口衍生出来了ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronouseQueue等集合。
其中最常用的的ArrayBlockQueue、LinkedBlockingQueue是通过
//ArrayBlockingQueue
final Object[] items;
final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
//LinkedBlockingQueue
static class Node<E>; //链表节点
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();
实现的。也就是一个生产消费者模式。并且它们是线程安全、在高并发的情况下推荐使用,代替arrayList、linkedList。
3、concurrentMap
ConcurrentHashMap:使用的是“分段锁”。同时使用segment来存放数据,每一个k都会更加hash算法,计算出不同的segment。在segment里面使用lock来加锁,直接将一个打得map集合,分散成很小的map数组,减少了占用时间。
concurrentHashMap的诞生用来在高并发的条件下代替hashTable(hashmap的synchronized版)。
应用:
ConcurrentMap concurrentMap = new ConcurrentHashMap();
concurrentMap.put("key", "value");
Object value = concurrentMap.get("key");
查看源码:通过实现一个静态嵌入内部类实现存储节点
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //key的hash值,用来快速定位查找的对象的位置
final K key;
volatile V val;
volatile Node<K,V> next;
}
并且,在兑取对象时并不会把整个Map锁住(通过unsafe的getObjectVolatile()方法)。此外,向其写入对象的时候,也不会将所有的数据锁住。
大概流程:concurrentHashMap会创建一个Node<K,V>[]出来存放Node<K,V>的链表地址。然后在存放、读取的时候通过神奇的unsafe指令(直接操作指令)来确定key.hash代表的Node<K,V>的地址,并且锁住它。这样就不会锁住整个concurrentHashMap了。如下图
4、Jakob jenkow的博客
concurent教程:http://tutorials.jenkov.com/java-util-concurrent/index.html
来源:oschina
链接:https://my.oschina.net/u/2246410/blog/648822