数据结构是程序的重要组成部分,我们总是需要管理存储在数据结构中的数据。数组、列表或者树是通用数据结构范例。Java API提供了大量随时可用的数据结构。但当处理并发应用时,由于Java API提供的所有结构并不都是线程安全的,所以需要非常谨慎使用。如果选择的数据结构不是线程安全的,那么应用中的数据可能不一致。

在并发应用中使用数据结构时,需要检查实现数据结构的类文档,确定是否支持并发操作。Java提供如下两种并发数据结构:

  • **非阻塞数据结构:**针对此类数据结构提供的诸如插入或者取出数据的所有操作,如果当前这些操作因为数据结构为满或者空而无法运行,则返回null值。
  • **阻塞数据结构:**此类数据结构提供的操作与非阻塞数据结构操作相同。但如果诸如插入或取出数据的操作没有立即执行,将会阻塞线程直到能够执行这些操作。

以下Java API提供能够在并发应用中使用的数据结构:

  • ConcurrentLinkedDeque:基于链接节点的非阻塞数据结构,在结构的起始或结尾插入数据。
  • LinkedBlockingDeque:基于链接节点的阻塞数据结构。有固定容量且在结构的起始或结尾插入元素,如果操作没有立即执行,将会阻塞线程直到能够执行这些操作。
  • ConcurrentLinkedQueue:非阻塞队列,在队尾插入元素,在起始位置取出元素。
  • ArrayBlockingQueue:具有固定长度的阻塞队列,在队尾插入元素,在起始位置取出元素。如果其提供的操作未执行,是因为队列已满或为空,则设置线程休眠,直到能够执行这些操作。
  • LinkedBlockingQueue:阻塞队列,在队尾插入元素,在起始位置取出元素。如果其提供的操作未执行,是因为队列已满或为空,则设置线程休眠,直到能够执行这些操作。
  • DelayQueue:包含延迟元素的LinkedBlockingQueue队列。每个插入此队列的元素必须实现Delayed接口,延迟值是0的元素才能从列表中取出。
  • LinkedTransferQueue:在实现类似生产者/消费者问题的情形中提供操作的阻塞队列。如果其提供的操作未执行,是因为队列已满或为空,则设置线程休眠,直到能够执行这些操作。
  • PriorityBlockingQueue:基于元素优先级排序的阻塞队列,所有插入此类队列的元素必须实现Comparable接口。通过compareTo()方法返回的值将确定队列中元素的位置。与所有阻塞数据结构相同,如果其提供的操作没有立即执行,则设置线程休眠,直到能够执行这些操作。
  • SynchronousQueue:阻塞队列,每次执行insert操作之前,必须等待其它线程执行remove操作。这两个操作必须同时执行。
  • ConcurrentHashMap:能够并发操作的HashMap,为非阻塞数据结构。
  • ConcurrentSkipListMap:此数据结构将键与值关联起来,每个键只能有唯一值。它按照顺序存储键,并提供方法查找元素以及从映射中取出元素。为非阻塞数据结构。

扩展学习

在并发应用中使用数据结构时,查看Java API文档选择最符合需求的数据结构。实现自定义数据结构会存在如下一些问题:

  • 具有复杂的内部结构
  • 需要考虑大量不同的情况
  • 需要进行大量测试确保运行正确

如果无法找到完全符合需求的数据结构,那么尝试充分扩展现有的并发数据结构来解决问题。