由于并行程序与串行程序的不同特点,适用于串行程序的一些数据结构可能无法直接在并发环境下正常工作,这是因为这些数据结构不是线程安全的。本节将着重介绍一些可以用于多线程环境的数据结构,如并发List、并发Set、并发Map等。

1.并发List

Vector 或者 CopyOnWriteArrayList 是两个线程安全的List实现,ArrayList 不是线程安全的。因此,应该尽量避免在多线程环境中使用ArrayList。如果因为某些原因必须使用的,则需要使用Collections.synchronizedList(List list)进行包装。

示例代码:

 List list = Collections.synchronizedList(new ArrayList()); ... synchronized (list) { Iterator i = list.iterator(); // 必须在同步块中 while (i.hasNext()) foo(i.next()); }

CopyOnWriteArrayList 的内部实现与Vector又有所不同。顾名思义,Copy-On-Write 就是 CopyOnWriteArrayList 的实现机制。即当对象进行写操作时,复制该对象;若进行的读操作,则直接返回结果,操作过程中不需要进行同步。

CopyOnWriteArrayList 很好地利用了对象的不变性,在没有对对象进行写操作前,由于对象未发生改变,因此不需要加锁。而在试图改变对象时,总是先获取对象的一个副本,然后对副本进行修改,最后将副本写回。

这种实现方式的核心思想是减少锁竞争,从而提高在高并发时的读取性能,但是它却在一定程度上牺牲了写的性能。

在 get() 操作上,Vector 使用了同步关键字,所有的 get() 操作都必须先取得对象锁才能进行。在高并发的情况下,大量的锁竞争会拖累系统性能。反观CopyOnWriteArrayList 的get() 实现,并没有任何的锁操作。

在 add() 操作上,CopyOnWriteArrayList 的写操作性能不如Vector,原因也在于Copy-On-Write。

在读多写少的高并发环境中,使用 CopyOnWriteArrayList 可以提高系统的性能,但是,在写多读少的场合,CopyOnWriteArrayList 的性能可能不如 Vector。

2.并发Set

和List相似,并发Set也有一个 CopyOnWriteArraySet ,它实现了 Set 接口,并且是线程安全的。它的内部实现完全依赖于 CopyOnWriteArrayList ,因此,它的特性和 CopyOnWriteArrayList 完全一致,适用于 读多写少的高并发场合,在需要并发写的场合,则可以使用 Set s = Collections.synchronizedSet(Set s)得到一个线程安全的Set。

示例代码:

 Set s = Collections.synchronizedSet(new HashSet()); ... synchronized (s) { Iterator i = s.iterator(); // 必须在同步块中 while (i.hasNext()) foo(i.next()); }

3.并发Map

在多线程环境下使用Map,一般也可以使用 Collections.synchronizedMap()方法得到一个线程安全的 Map(详见示例代码1)。但是在高并发的情况下,这个Map的性能表现不是最优的。由于 Map 是使用相当频繁的一个数据结构,因此 JDK 中便提供了一个专用于高并发的 Map 实现 ConcurrentHashMap。

Collections的示例代码1:

 Map m = Collections.synchronizedMap(new HashMap()); ... Set s = m.keySet(); // 不需要同步块 ... synchronized (m) { // 同步在m上,而不是s上!! Iterator i = s.iterator(); // 必须在同步块中 while (i.hasNext()) foo(i.next()); }

1.为什么不能在高并发下使用HashMap?

因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。

2.为什么不使用线程安全的HashTable?

HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态。如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。

3.ConcurrentHashMap的优势

ConcurrentHashMap的内部实现进行了锁分离(或锁分段),所以它的锁粒度小于同步的 HashMap;同时,ConcurrentHashMap的 get() 操作也是无锁的。

锁分离:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。

上述文字部分参考自文章:Java集合---ConcurrentHashMap原理分析

4.并发Queue

在并发队列上,JDK提供了两套实现,一个是以 ConcurrentLinkedQueue 为代表的高性能队列,一个是以 BlockingQueue 接口为代表的阻塞队列。不论哪种实现,都继承自 Queue 接口。

ConcurrentLinkedQueue 是一个适用于高并发场景下的队列。它通过无锁的方式,实现了高并发状态下的高性能。通常,ConcurrentLinkedQueue 的性能要好于 BlockingQueue 。

与 ConcurrentLinkedQueue 的使用场景不同,BlockingQueue 的主要功能并不是在于提升高并发时的队列性能,而在于简化多线程间的数据共享。

BlockingQueue 典型的使用场景是生产者-消费者模式,生产者总是将产品放入 BlockingQueue 队列,而消费者从队列中取出产品消费,从而实现数据共享。

BlockingQueue 提供一种读写阻塞等待的机制,即如果消费者速度较快,则 BlockingQueue 则可能被清空,此时消费线程再试图从 BlockingQueue 读取数据时就会被阻塞。反之,如果生产线程较快,则 BlockingQueue 可能会被装满,此时,生产线程再试图向 BlockingQueue 队列装入数据时,便会被阻塞等待,其工作模式如图所示。

5.并发Deque

在JDK1.6中,还提供了一种双端队列(Double-Ended Queue),简称Deque。Deque允许在队列的头部或尾部进行出队和入队操作。与Queue相比,具有更加复杂的功能。

Deque 接口的实现类:LinkedList、ArrayDeque和LinkedBlockingDeque。

它们都实现了双端队列Deque接口。其中LinkedList使用链表实现了双端队列,ArrayDeque使用数组实现双端队列。通常情况下,由于ArrayDeque基于数组实现,拥有高效的随机访问性能,因此ArrayDeque具有更好的遍性能。但是当队列的大小发生变化较大时,ArrayDeque需要重新分配内存,并进行数组复制,在这种环境下,基于链表的 LinkedList 没有内存调整和数组复制的负担,性能表现会比较好。但无论是LinkedList或是ArrayDeque,它们都不是线程安全的。

LinkedBlockingDeque 是一个线程安全的双端队列实现。可以说,它已经是最为复杂的一个队列实现。在内部实现中,LinkedBlockingDeque 使用链表结构。每一个队列节点都维护了一个前驱节点和一个后驱节点。LinkedBlockingDeque 没有进行读写锁的分离,因此同一时间只能有一个线程对其进行操作。因此,在高并发应用中,它的性能表现要远远低于 LinkedBlockingQueue,更要低于 ConcurrentLinkedQueue 。

作者:像风一样原文:https://www.cnblogs.com/yueshutong/p/9696216.html

java list 转 map_高并发下的Java数据结构(List、Set、Map、Queue)相关推荐

  1. java 并发 set_高并发下的Java数据结构(List、Set、Map、Queue)

    1.并发List Vector 或者 CopyOnWriteArrayList 是两个线程安全的List实现,ArrayList 不是线程安全的.因此,应该尽量避免在多线程环境中使用ArrayList ...

  2. java 抽奖 高并发_【java】如何设计高并发下的抽奖?

    我写的伪代码如下,但出现了个bug,抽奖现在要限制每日抽奖结果出现的次数,但实际运行是在并发时不能限制住,如何解决? resultDayLimitTimes = { resultA => 2 # ...

  3. java应用程序占用高内存_对Java应用程序中的内存问题进行故障排除

    java应用程序占用高内存 重要要点 解决内存问题可能很棘手,但是正确的方法和正确的工具集可以大大简化此过程. Java HotSpot JVM可以报告几种OutOfMemoryError消息,因此务 ...

  4. 第一百期:Java架构师:高并发下的流量控制

    这个时候如果不做任何保护措施,服务器就会承受很大的处理压力,请求量很高,服务器负载也很高,并且当请求超过服务器承载极限的时候,系统就会崩溃,导致所有人都不能访问. 作者:IT技术分享 这个时候如果不做 ...

  5. java语法糖效率高吗_打包 Java将持续向“高糖”方向发展,你真的了解Java语法糖吗? _好机友...

    Java语法糖概念 1. 语法糖Syntactic Sugar 糖衣语法,方便开发人员使用,JVM并不识别,会在编译阶段解语法糖,还原为基础语法. 2. com.sun.tools.javac.mai ...

  6. java tps 优化_高tps下,java性能调优

    之前参与了系统调优,感觉挺有收获了,现在记录一下. 处理xml时,理清楚报文格式,避免使用jdom解析 我们在做压测的时候,发现有一个程序响应非常慢,使用jstack定位到慢的地方:解析xml报文.x ...

  7. 高并发下如何生成随机数

    在平时的开发中我们经常会用到随机数,比如使用new Random().Math.random()等生成,然而在高并发环境中(比如电商项目,中间件系统等)使用上面的方法并不是最优的,会影响系统性能.那么 ...

  8. java时间往后一天_如何在Java中将日期增加一天?

    24个解决方案 617 votes 像这样的东西应该做的伎俩: String dt = "2008-01-01"; // Start date SimpleDateFormat s ...

  9. java创新_Java没有创新了吗?Java 13提供可提高生产率和效率的功能

    Java没有创新了吗? 在其CodeOne会议上,Oracle解释了Java SE 13正式发布后Java的快速发布周期如何带来创新. 在过去的二十多年中,Java编程语言新版本的开发速度相对较慢,仅 ...

最新文章

  1. 【斩获7枚offer,入职阿里平台事业部】横扫阿里、美团、京东、 去哪儿之后,写下了这篇面经!
  2. [html] 对一个元素设置浮动后,它的特征是什么?
  3. 前端学习(2783):封装myrequest并绑定到全局
  4. 八大看点丨第十届数据技术嘉年华精彩抢先速览
  5. 解决 IDEA 中src下xml等资源文件无法读取的问题
  6. Asp.Net alert弹出提示信息的若干种方法
  7. html5中关于input使用方法的改变
  8. Python-Numpy语法总结-数组的创建
  9. 红外接收管硬件电路曲折的调试过程,错误的使用过程记录
  10. 蒸汽管道图纸符号_供热循环系统“30问”(附管网图常见符号图例)
  11. android工程如何创建数据库,安卓项目-利用Sqlite数据库,开发新闻发布系统
  12. Badboy提示脚本错误解决方法
  13. 一个基本c语言注释用什么字符串,C语言的词法规则京鸿智武 今天提纲:本文主要介绍了C语言中...
  14. 笑脸检测笑脸识别微笑识别大笑识别
  15. linux 串口格式化输出字符串,glibc中的printf如何输出到串口
  16. 如何用 Python 让你的 PPT 数据动起来?
  17. C语言基础知识:C语言函数调用怎么返回两个值
  18. Win10改用microsoft账户登录发生了错误怎么解决?
  19. 安卓Android/微信小程序的驾校考试预约管理系统APP
  20. 可视化仪表板是什么_使用Speculo在线可视化调色板

热门文章

  1. g20曲线拟合源码解读
  2. 运维少年系列 python and cisco (1)
  3. 浅谈 JSON.stringify 方法
  4. RabbitMQ 四种Exchange
  5. 2013年7月29日周一
  6. HTML之六:图像的热区连接
  7. XML 新手最佳入门教程
  8. Java Selenium Actions模拟鼠标拖动dragAndDrop总结
  9. MyEclipse使用总结——设置MyEclipse开发项目时使用的JDK
  10. 理解SQL SERVER中的分区表