Java多线程编程四 并发容器

并发容器小结

并发包中的 List--CopyOnWriteArrayList

并发包中的 List 只有 CopyOnWriteArrayList ,是一个线程安全的 ArrayList ,对其的修改都是在底层的一个复制数组(快照)上进行的,使用了写时复制的策略。这种实现只是保证数据的最终一致性,在添加到拷贝数据而还没进行替换的时候,读到的仍然是旧数据。如果对象比较大,频繁地进行替换会消耗内存,从而引发Java的GC问题,这个时候,我们应该考虑其他的容器,例如ConcurrentHashMap。

写时复制策略

核心思想是,如果有多个调用者(Callers)同时要求相同的资源(如内存或者是磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者视图修改资源内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此做法主要的优点是如果调用者没有修改资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。

使用场景

CopyOnWrite并发容器用于读多写少的并发场景。比如白名单,黑名单,商品类目的访问和更新场景,假如我们有一个搜索网站,用户在这个网站的搜索框中,输入关键字搜索内容,但是某些关键字不允许被搜索。这些不能被搜索的关键字会被放在一个黑名单当中,黑名单每天晚上更新一次。当用户搜索时,会检查当前关键字在不在黑名单当中,如果在,则提示不能搜索。

弱一致性

add() 方法添加元素,通过加独占锁 ReentrantLock 保证数据原子性操作,其中在添加元素的时候,首先复制了一个快照,然后在快照上进行添加,替换之前的数组,所以它是无界的。

remove() 方法也获取了独占锁,但是所有的读数据操作未加锁,产生了弱一致性问题--随着时间节点发展,结果逐渐一致。

CopyOnWriteArraySet 的底层是 CopyOnWriteArrayList 。

并发队列

并发队列,按照实现方式的不同可分为阻塞队列和非阻塞队列,前者使用锁实现,后者使用 CAS 非阻塞算法实现,线程安全。

ConcurrentLinkedQueue

内部的队列使用单向链表,有两个 volatile 的Node节点分别存放首位节点,通过 CAS 非阻塞算法保证出入队时操作链表的原子性。

//构造方法
public ConcurrentLinkedQueue(){
    head = tail = new Node<E>(null);
}
//主要方法

//添加元素,二者一致,非阻塞方法,若传参为null,将报空指针异常
public boolean offer(E e);
public boolean add(E e);

//移除元素
public E poll();//在队列头部获取元素并将其移除,若队列为空则返回null;
public boolean remove(Object o);//移除元素

//查询队列元素
public E peek();//在队列头部获取元素,若队列为空则返回null;
public int size();//由于 CAS 没有加锁,在并发条件下不是很有用
public boolean contains(Object o);//查找该元素

LinkedBlockingQueue

有界链表实现有界阻塞队列,借助锁实现,线程安全,使用了 生产者-消费者 模式

//主要方法

public boolean offer(E e);//非阻塞添加元素,若队列已满则丢弃当前元素返回false,元素空指针检查
public boolean put(E e)throws InterruptedException;//阻塞添加元素,队列若满,则等待队列空闲后插入;在阻塞时被设置中断标志,会抛出异常而返回,元素参数空指针检查

//移除元素
public E poll();//在队列头部获取元素并将其移除,若队列为空则返回null;不阻塞
public boolean remove(Object o);//移除元素
public E take() throws InterruptedException;//阻塞获取并移除元素,在阻塞时被设置中断标志,会抛出异常而返回,元素参数空指针检查

//查询队列元素
public E peek();//在队列头部获取元素,若队列为空则返回null;不阻塞
public int size();//count变量被加锁,比较准确

ArrayBlockingQueue

有界数组实现有界阻塞队列;数组 items 存放队列元素,count统计元素个数,通过加独占锁实现出入队操作的原子性,条件变量 notEmpty,notFull 进行出入队的同步。

//主要方法

public boolean offer(E e);//非阻塞添加元素,若队列已满则丢弃当前元素返回false,元素空指针检查
public boolean put(E e)throws InterruptedException;//阻塞添加元素,队列若满,则等待队列空闲后插入;在阻塞时被设置中断标志,会抛出异常而返回,元素参数空指针检查

//获取或移除元素
public E poll();//在队列头部获取元素并将其移除,若队列为空则返回null;不阻塞
public E peek();//在队列头部获取元素,若队列为空则返回null;不阻塞
public E take() throws InterruptedException;//阻塞获取并移除元素,在阻塞时被设置中断标志,会抛出异常而返回,元素参数空指针检查

//查询队列元素
public int size();//count变量操作修改时被加全局锁,精确

PriorityBlockingQueue

带优先级的无界阻塞队列,每次出队都是优先级最高或最低的元素,内部是二叉平衡树堆,元素必须实现 Comparable 接口或 传入比较器。

//主要方法

public boolean offer(E e);//添加元素,元素空指针检查
public boolean put(E e);//添加元素,元素空指针检查

//获取或移除元素
public E poll();//获取根部元素并将其移除,若队列为空则返回null;不阻塞
public E take() throws InterruptedException;//队列为空则阻塞,阻塞获取并移除元素,在阻塞时被设置中断标志,会抛出异常而返回,元素参数空指针检查

//查询队列元素
public int size();//count变量操作修改时被加全局锁,精确

DelayQueue

无界阻塞延迟队列,队列中每个元素都有过期时间,只有过期元素才会出队,队列头元素为最快要过期元素,内部使用优先队列(堆)存放元素。

//主要方法

public boolean offer(E e);//添加元素,元素空指针检查

//获取或移除元素
public E poll();//获取头部过期元素并将其移除,若队列为空则返回null;不阻塞
public E take() throws InterruptedException;//队列没有过期元素则阻塞,阻塞获取并移除元素,在阻塞时被设置中断标志,会抛出异常而返回,元素参数空指针检查

//查询队列元素
public int size();//包含过期与未过期的元素

ConcurrentHashMap

和HashMap类似;

ConcurrentHashMap的底层实现为 数组+链表+红黑树(通过链地址法解决冲突);

使用CAS与synchronized实现并发安全;

不可以存放空键值

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353