前言:
在日常开发中,我们面临着越来越多的并发计算需求。应用程序需要能够处理大规模的并发请求、保证高性能和可靠性,并且能够适应不断增长的用户量。本文总结了一些好用高级的并发工具,为并发问题提供更多解决方案。
一、超好用的并发集合
1.ConcurrentHashMap:
ConcurrentHashMap
是 Java 中的一个线程安全的哈希表实现,它是 HashMap
的一个并发版本。ConcurrentHashMap
提供了高效的并发访问,并保证在多线程环境下的可靠性。
ConcurrentHashMap
的特点如下:
-
线程安全:
ConcurrentHashMap
是线程安全的,多个线程可以同时读取和写入,而无需额外的同步机制(如锁)。它使用分段锁(Segment)来实现线程安全,不同的线程可以同时访问不同的段,从而提高并发性能。 -
高效性能:
ConcurrentHashMap
在并发访问的情况下仍然保持良好的性能。通过将哈希表分成多个段(Segment),每个段都有自己的锁,不同的线程可以同时访问不同的段,从而减少了竞争。这使得多个线程可以并发地读取和写入,而不会造成阻塞。 -
可调整的并发级别:
ConcurrentHashMap
允许你通过指定并发级别来控制内部段(Segment)的数量。并发级别是指可以同时更新的线程数。默认情况下,并发级别为 16,但你可以根据应用程序的需求进行调整。 -
支持高效的扩容:
ConcurrentHashMap
在扩容时可以避免全局锁,而是只对需要扩容的段进行锁定,从而减少了并发操作的影响。 -
支持高效的迭代:
ConcurrentHashMap
提供了多种遍历和迭代的方法,包括keySet()
、values()
和entrySet()
等。这些方法在多线程环境下仍然保持一致的快照视图,不会被其他线程的修改所影响。
2.CopyOnWriteArrayList:
CopyOnWriteArrayList
是 Java 中的一个线程安全的列表实现,它是 ArrayList
的一个并发版本。与普通的列表实现不同,CopyOnWriteArrayList
在修改操作时会创建一个全新的副本,以保证线程安全性。
-
线程安全:
CopyOnWriteArrayList
是线程安全的,多个线程可以同时读取而无需额外的同步机制。它通过在修改操作时创建一个全新的副本来保证线程安全性,从而避免了读写操作的竞争问题。 -
写时复制:当需要进行修改操作(如添加、删除元素)时,
CopyOnWriteArrayList
会创建一个当前列表的副本,并在副本上进行修改操作。这样,正在进行读取操作的线程仍然可以访问原始列表,而不会受到修改操作的影响。 -
高效的读取操作:由于读取操作不需要进行任何同步措施,
CopyOnWriteArrayList
在并发读取的情况下具有较高的性能。多个线程可以同时进行读取操作,而不会发生竞争或冲突。 -
适用于读多写少的场景:
CopyOnWriteArrayList
在读多写少的场景下表现良好。由于每次修改都需要创建一个副本,所以在写入操作频繁的情况下,性能可能会受到影响。 -
迭代器的弱一致性:
CopyOnWriteArrayList
的迭代器提供了弱一致性的保证。迭代器在创建时会获取一个快照,并对快照进行遍历。因此,迭代器不会受到其他线程对列表的修改所影响。
3.CopyOnWriteArrayList
:
ConcurrentLinkedQueue : ConcurrentLinkedQueue
是 Java 中的一个线程安全的非阻塞队列实现,它使用链表的方式存储元素。ConcurrentLinkedQueue
提供了高效的并发访问,并保证在多线程环境下的可靠性。
ConcurrentLinkedQueue
的特点如下:
-
线程安全:
ConcurrentLinkedQueue
是线程安全的,多个线程可以同时进行插入和删除操作,而无需额外的同步机制。它使用无锁算法和 CAS(Compare and Swap)操作来实现线程安全。 -
高效性能:
ConcurrentLinkedQueue
在并发访问的情况下仍然保持良好的性能。由于它使用链表来存储元素,插入和删除操作的时间复杂度为 O(1),并且不会发生线程阻塞。 -
非阻塞算法:
ConcurrentLinkedQueue
使用非阻塞算法来实现并发访问。它使用 CAS 操作来执行插入和删除操作,避免了使用锁带来的竞争和阻塞。 -
无界队列:
ConcurrentLinkedQueue
是一个无界队列,它没有容量限制。可以根据需要动态添加和删除元素。 -
支持迭代器:
ConcurrentLinkedQueue
提供了迭代器来遍历队列中的元素。迭代器提供弱一致性的保证,在迭代过程中可能会看到其他线程对队列的修改。
4.BlockingQueue
BlockingQueue
是 Java 中的一个接口,表示一个支持阻塞操作的队列。它是一个线程安全的队列,提供了在队列为空或已满时进行阻塞的功能。
BlockingQueue
的特点如下:
-
阻塞操作:
BlockingQueue
提供了一系列阻塞操作,包括在队列为空时等待元素的到来以及在队列已满时等待空间释放。它允许线程在队列操作无法立即执行时进行阻塞,而不是忙等或轮询。 -
线程安全:
BlockingQueue
是线程安全的,多个线程可以同时进行插入和删除操作,而无需额外的同步机制。它提供了内部的同步机制来保证线程安全。 -
支持有界与无界队列:
BlockingQueue
可以是有界队列或无界队列。有界队列在达到容量限制时,插入操作会被阻塞,直到有空间可用。而无界队列没有容量限制,插入操作永远不会被阻塞。 -
支持阻塞和超时操作:
BlockingQueue
提供了阻塞和超时操作的方法,如put()
、take()
、offer()
和poll()
等。这些方法允许线程在队列操作无法立即执行时进行阻塞,并可以设置等待的超时时间。 -
支持生产者-消费者模式:
BlockingQueue
是实现生产者-消费者模式的常用工具。生产者线程可以向队列中插入元素,而消费者线程可以从队列中取出元素,两者之间通过BlockingQueue
进行同步和通信。
常见的实现类有 ArrayBlockingQueue
、LinkedBlockingQueue
、PriorityBlockingQueue
等。
5.ConcurrentSkipListMap :
ConcurrentSkipListMap
是 Java 中的一个线程安全的有序映射表(SortedMap)实现,它基于跳表(Skip List)的数据结构。ConcurrentSkipListMap
提供了高效的并发访问,并保证在多线程环境下的可靠性。
ConcurrentSkipListMap
的特点如下:
-
线程安全:
ConcurrentSkipListMap
是线程安全的,多个线程可以同时进行插入、删除和查询操作,而无需额外的同步机制。它使用乐观并发控制和 CAS(Compare and Swap)操作来实现线程安全。 -
有序映射表:
ConcurrentSkipListMap
是有序映射表,它按照键的顺序进行排序。这使得可以根据键的顺序进行范围查询和遍历操作。 -
跳表数据结构:
ConcurrentSkipListMap
使用跳表的数据结构来实现高效的并发访问。跳表是一种平衡的数据结构,可以在有序链表的基础上快速进行插入、删除和查询操作。 -
可调整的并发级别:
ConcurrentSkipListMap
允许你通过指定并发级别来控制内部数据结构的并发度。并发级别是指可以同时更新的线程数。默认情况下,并发级别为 16,但你可以根据应用程序的需求进行调整。 -
高效的并发访问:
ConcurrentSkipListMap
在并发访问的情况下仍然保持良好的性能。它使用乐观并发控制和无锁算法来减少竞争,从而提高并发性能。
二、无锁缓存框架:Disruptor
Disruptor 是一个高性能的无锁并发编程框架,用于解决在多线程环境下的高并发问题。它使用了一种称为环形缓冲区的数据结构,通过使用预分配的内存来实现高效的并发操作。
Disruptor 的特点如下:
-
高性能:Disruptor 的设计目标是提供极高的吞吐量和低延迟。它使用无锁算法和内存预分配的方式,避免了锁竞争和动态内存分配带来的性能损失。
-
环形缓冲区:Disruptor 使用环形缓冲区作为数据结构,多个生产者线程可以同时向缓冲区中写入数据,多个消费者线程可以同时从缓冲区中读取数据。这种设计方式减少了线程之间的竞争,提高了并发性能。
-
事件驱动模型:Disruptor 的核心概念是事件(Event)和事件处理器(EventProcessor)。生产者线程将事件放入缓冲区,然后由消费者线程的事件处理器进行处理。事件处理器可以是单个消费者,也可以是多个消费者。
-
低延迟:Disruptor 的无锁算法和预分配内存的方式使得事件的处理延迟非常低。它适用于需要实时处理和低延迟的应用场景,如金融交易系统和网络服务器。
-
可扩展性:Disruptor 的设计允许将多个生产者线程和多个消费者线程进行组合,以满足不同的并发需求。它支持多生产者、多消费者以及多个消费者之间的事件依赖关系。
三、读写锁的改进:读写锁的改进
StampedLock
是 Java 8 中引入的一种乐观读写锁(Optimistic Read-Write Lock),它提供了三种模式的锁:读锁、写锁和乐观读锁。它的设计目标是在读多写少的场景中提供更好的性能。
StampedLock
的特点如下:
-
三种模式的锁:
StampedLock
提供了三种模式的锁:读锁、写锁和乐观读锁。读锁和写锁的使用方式与传统的读写锁类似,而乐观读锁是一种特殊的读锁,不会阻塞其他线程的写操作。 -
乐观读锁:乐观读锁是
StampedLock
的一种特性,它不会阻塞其他线程的写操作。线程在获取乐观读锁后可以继续执行,但在实际读取共享数据之前需要验证版本戳是否发生了变化。如果版本戳发生了变化,则需要重新获取读锁或采取其他适当的处理。 -
无重入性:
StampedLock
不支持重入性,即同一个线程多次获取同一种模式的锁会导致死锁。这是为了避免死锁风险,所以在使用StampedLock
时需要特别注意。 -
高性能:
StampedLock
在读多写少的场景中可以提供更好的性能。乐观读锁不会阻塞其他线程的写操作,而读锁和写锁的性能也优于传统的读写锁。 -
不支持条件变量:
StampedLock
不支持条件变量,因此无法使用Condition
对象进行线程间的等待和通知操作。
四、原子类增加
1.LongAdder
LongAdder
是 Java 8 中引入的一种高效的原子累加器,用于在高并发情况下对一个变量进行累加操作。它是 AtomicLong
的替代品,可以在高度并发的情况下提供更好的性能。
LongAdder
的特点如下:
-
高并发性能:
LongAdder
的设计目标是在高并发情况下提供更好的性能。它通过将累加操作分散到多个变量(称为 "cell")上,从而减少了线程之间的竞争。每个线程会累加自己负责的变量,最后将所有变量的值汇总得到最终结果。 -
无锁算法:
LongAdder
使用了无锁算法,避免了在高并发情况下的锁竞争问题。它利用 CAS(Compare and Swap)操作和 volatile 变量实现了线程安全的原子操作。 -
分段累加:
LongAdder
将累加操作分段到多个变量上,每个变量负责一部分累加操作。这样可以减少线程之间的竞争,提高并发性能。当需要获取累加结果时,所有变量的值会被汇总计算得到最终结果。 -
减少冲突:
LongAdder
通过将累加操作分散到多个变量上,减少了线程之间的竞争和冲突。这种设计方式在高并发情况下可以更好地利用多核处理器的优势,提高性能。 -
懒惰求值:
LongAdder
在获取累加结果时,会尽可能地延迟求值。这意味着在没有必要获取累加结果时,不会进行计算,从而减少了性能开销。
2.LongAccumulator
LongAccumulator
是 Java 8 中引入的一种高级原子累加器,用于对一个变量进行累加操作,并且可以使用自定义的累加函数。它是 LongAdder
的扩展,提供了更灵活的累加操作。
LongAccumulator
的特点如下:
-
灵活的累加函数:
LongAccumulator
允许使用自定义的累加函数进行累加操作。累加函数接收两个参数:当前的累加结果和新的值,并返回一个新的累加结果。这使得LongAccumulator
可以进行更复杂的累加操作,如求最大值、最小值或其他自定义的累加逻辑。 -
原子性操作:
LongAccumulator
提供了原子性的累加操作,保证在多线程环境下的线程安全性。多个线程可以同时对累加器进行累加操作,而不需要额外的同步机制。 -
初始值:
LongAccumulator
可以指定一个初始值,作为累加的起点。如果没有指定初始值,则使用默认值 0。 -
灵活的累加器:
LongAccumulator
提供了一种灵活的方式来组合多个累加器。通过使用accumulate()
方法和自定义的累加函数,可以将多个LongAccumulator
实例组合成一个更复杂的累加器。
五、分布式计算框架
Akka 是一个开源的分布式计算框架,用于构建高并发、可扩展和容错的分布式应用程序。它基于 Actor 模型,提供了强大的并发编程抽象和工具,使得开发人员可以轻松地编写并发和分布式应用。
Akka 的特点如下:
-
Actor 模型:Akka 基于 Actor 模型,将并发计算的基本单元抽象为 Actor。Actor 是一个轻量级的计算实体,可以接收消息、进行计算和发送消息给其他 Actor。每个 Actor 都有自己的状态和行为,通过消息传递进行通信和协作。
-
异步消息传递:Akka 使用异步消息传递来实现并发和分布式通信。消息是通过邮箱(Mailbox)进行传递,每个 Actor 都有自己的邮箱,接收到的消息按顺序进行处理。这种异步消息传递方式能够提高应用程序的并发性能和响应性。
-
容错机制:Akka 提供了强大的容错机制,使得应用程序能够在出现故障时保持可靠和稳定。它支持监督(Supervision)模式,当一个 Actor 发生故障时,可以由其监督者(Supervisor)进行处理,如重启、停止或继续运行。
-
高可扩展性:Akka 的设计目标之一是提供高可扩展性。它支持将应用程序水平扩展到多台服务器上,通过分布式部署和路由策略来实现负载均衡和容量管理。
-
轻量级和高性能:Akka 是一个轻量级框架,具有很高的性能。它采用了非阻塞的 I/O 模型,使用事件驱动的方式处理消息和计算,以最大程度地提高吞吐量和响应速度