首页 > 数据库 > 数据库开发技术 > 7大并发容器种类原理解析与应用
在Java中,并发容器是专门设计用于多线程环境的数据结构,能够提供线程安全的操作。以下是7大并发容器的种类及其简要介绍:
这些容器通过不同的锁机制和算法来实现线程安全,从而在多线程环境中提供高效的数据访问和操作。
下面来V哥挨个分析一下源码实现原理和应用场景。
ConcurrentHashMap 是 Java 中用于处理并发数据的哈希表,其设计目标是提供高性能的并发访问。以下是其主要实现原理:
JDK 1.7 版本
JDK 1.8 版本之后
业务应用场景与代码案例
场景:缓存系统
在高并发系统中,缓存是常见的需求。使用 ConcurrentHashMap 可以实现高效的缓存存储和访问。
import java.util.concurrent.ConcurrentHashMap;
public class CacheSystem {
private ConcurrentHashMap<String, String> cacheMap;
public CacheSystem() {
cacheMap = new ConcurrentHashMap<>();
}
public void putToCache(String key, String value) {
cacheMap.put(key, value);
}
public String getFromCache(String key) {
return cacheMap.get(key);
}
public void removeFromCache(String key) {
cacheMap.remove(key);
}
public static void main(String[] args) {
CacheSystem cacheSystem = new CacheSystem();
// 模拟并发写入缓存
for (int i = 0; i < 1000; i++) {
final int index = i;
new Thread(() -> cacheSystem.putToCache("key" + index, "value" + index)).start();
}
// 模拟并发读取缓存
for (int i = 0; i < 1000; i++) {
final int index = i;
new Thread(() -> {
String value = cacheSystem.getFromCache("key" + index);
System.out.println("Key: key" + index + ", Value: " + value);
}).start();
}
}
}
在这个示例中,CacheSystem 类使用 ConcurrentHashMap 来存储缓存数据。通过多线程模拟并发写入和读取缓存的操作,展示了 ConcurrentHashMap 在高并发环境下的性能优势。
场景:分布式锁
在分布式系统中,有时需要实现分布式锁来控制对共享资源的访问。可以使用 ConcurrentHashMap 来实现一个简单的分布式锁。
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class DistributedLock {
private ConcurrentHashMap<String, Boolean> lockMap;
public DistributedLock() {
lockMap = new ConcurrentHashMap<>();
}
public boolean tryLock(String key) {
return lockMap.putIfAbsent(key, true) == null;
}
public void unlock(String key) {
lockMap.remove(key);
}
public static void main(String[] args) throws InterruptedException {
DistributedLock distributedLock = new DistributedLock();
// 模拟并发访问资源
for (int i = 0; i < 10; i++) {
final int index = i;
new Thread(() -> {
if (distributedLock.tryLock("resource")) {
try {
System.out.println("Thread " + index + " acquired the lock.");
TimeUnit.SECONDS.sleep(1); // 模拟资源访问
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
distributedLock.unlock("resource");
System.out.println("Thread " + index + " released the lock.");
}
} else {
System.out.println("Thread " + index + " failed to acquire the lock.");
}
}).start();
}
}
}
在这个示例中,DistributedLock 类使用 ConcurrentHashMap 来实现分布式锁。通过 putIfAbsent 方法尝试获取锁,如果成功则执行资源访问操作,访问完成后释放锁。
ConcurrentSkipListMap 是 Java java.util.concurrent 包中提供的一个并发集合类,它实现了 SortedMap 接口。它是基于跳表(Skip List)的数据结构实现的,跳表是一种可以替代平衡树的数据结构,通过多层链表实现快速的查找、插入和删除操作。
跳表原理
ConcurrentSkipListMap 的实现细节的关键点
业务应用场景
ConcurrentSkipListMap 适用于需要有序访问的数据集合,以下是一些典型的应用场景:
应用代码案例
以下是一个使用 ConcurrentSkipListMap 的示例,展示如何在高并发环境下存储和访问有序数据。
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger;
public class ConcurrentSkipListMapExample {
private static final ConcurrentSkipListMap<Integer, String> map = new ConcurrentSkipListMap<>();
public static void main(String[] args) {
// 模拟并发写入
for (int i = 0; i < 10000; i++) {
final int index = i;
new Thread(() -> {
map.put(index, "Value" + index);
System.out.println("Inserted: Key=" + index + ", Value=" + map.get(index));
}).start();
}
// 模拟并发读取
for (int i = 0; i < 10000; i++) {
final int index = i;
new Thread(() -> {
String value = map.get(index);
if (value != null) {
System.out.println("Retrieved: Key=" + index + ", Value=" + value);
}
}).start();
}
}
}
在这个示例中:
由于 ConcurrentSkipListMap 是线程安全的,因此在多线程环境下可以安全地进行读写操作,而不需要额外的同步控制。
ConcurrentSkipListMap 通过跳表和细粒度的锁机制实现了高效的并发访问和有序性,适用于需要高并发读写和有序数据存储的业务场景。
ConcurrentLinkedQueue 是 Java 并发包 java.util.concurrent 中提供的一个无界线程安全队列,它基于链表实现。以下是其主要实现原理是这样的:
业务应用场景
ConcurrentLinkedQueue 适用于以下业务场景:
应用代码案例
以下是一个使用 ConcurrentLinkedQueue 的示例,展示如何在生产者-消费者模型中使用它。
import java.util.concurrent.ConcurrentLinkedQueue;
public class ConcurrentLinkedQueueExample {
private static final ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
public static void main(String[] args) {
// 生产者线程
for (int i = 0; i < 10; i++) {
new Thread(() -> {
queue.offer(i); // 入队
System.out.println("Produced: " + i);
}).start();
}
// 消费者线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
while (true) {
Integer item = queue.poll(); // 出队
if (item == null) {
break;
}
System.out.println("Consumed: " + item);
}
}).start();
}
}
}
在这个示例中:
由于 ConcurrentLinkedQueue 是线程安全的,因此生产者和消费者线程可以并发地访问队列,而不需要额外的同步控制。
ConcurrentLinkedQueue 通过链表和 CAS 操作实现了高效的并发访问,适用于需要高并发读写的队列操作。它在生产者-消费者模型、任务调度、日志收集等场景中具有广泛的应用。
ConcurrentLinkedDeque 是 Java java.util.concurrent 包中的一个并发双端队列实现。它是一个无界队列,支持从队列的头部或尾部进行高效的并发插入、删除和访问操作。以下是其主要实现原理:
业务应用场景
ConcurrentLinkedDeque 适用于以下业务场景:
应用代码案例
以下是一个使用 ConcurrentLinkedDeque 的示例,展示如何在生产者-消费者模型中使用它。
import java.util.concurrent.ConcurrentLinkedDeque;
public class ConcurrentLinkedDequeExample {
private static final ConcurrentLinkedDeque<Integer> deque = new ConcurrentLinkedDeque<>();
public static void main(String[] args) {
// 生产者线程
for (int i = 0; i < 10; i++) {
new Thread(() -> {
deque.offer(i); // 入队
System.out.println("Produced: " + i);
}).start();
}
// 消费者线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
while (true) {
Integer item = deque.poll(); // 从头部出队
if (item == null) {
break;
}
System.out.println("Consumed from head: " + item);
item = deque.pollLast(); // 从尾部出队
if (item == null) {
break;
}
System.out.println("Consumed from tail: " + item);
}
}).start();
}
}
}
在这个示例中:
由于 ConcurrentLinkedDeque 是线程安全的,因此生产者和消费者线程可以并发地访问队列,而不需要额外的同步控制。
ConcurrentLinkedDeque 通过双向链表和 CAS 操作实现了高效的并发访问,支持从队列的两端进行插入和删除操作。它在生产者-消费者模型、任务调度、日志收集等场景中具有广泛的应用。
ArrayBlockingQueue 是 Java java.util.concurrent 包中的一个有界阻塞队列实现。它基于数组实现,可以指定队列的容量。以下是其主要实现原理是这样的:
业务应用场景
ArrayBlockingQueue 适用于以下业务场景:
应用代码案例
以下是一个使用 ArrayBlockingQueue 的示例,展示如何在生产者-消费者模型中使用它。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ArrayBlockingQueueExample {
private static final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10); // 队列容量为10
public static void main(String[] args) {
// 生产者线程
for (int i = 0; i < 15; i++) {
new Thread(() -> {
try {
queue.put(i); // 入队
System.out.println("Produced: " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
// 消费者线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
while (true) {
Integer item = queue.take(); // 从队列中取出元素
System.out.println("Consumed: " + item);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
在这个示例中:
由于 ArrayBlockingQueue 是线程安全的,因此生产者和消费者线程可以并发地访问队列,而不需要额外的同步控制。
ArrayBlockingQueue 通过数组和锁机制实现了有界阻塞队列,支持从队列的两端进行插入和删除操作。它在生产者-消费者模型、任务调度、数据缓冲等场景中具有广泛的应用。
LinkedBlockingQueue 是 Java java.util.concurrent 包中的一个阻塞队列实现。与 ArrayBlockingQueue 不同的是,LinkedBlockingQueue 使用链表而非数组来存储元素,因此它可以是一个有界队列也可以是一个无界队列。以下是其主要实现原理关键点:
业务应用场景
LinkedBlockingQueue 适用于以下业务场景:
应用代码案例
以下是一个使用 LinkedBlockingQueue 的示例,展示如何在生产者-消费者模型中使用它。
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class LinkedBlockingQueueExample {
private static final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(); // 无界队列
public static void main(String[] args) {
// 生产者线程
for (int i = 0; i < 15; i++) {
new Thread(() -> {
try {
queue.put(i); // 入队
System.out.println("Produced: " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
// 消费者线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
while (true) {
Integer item = queue.take(); // 从队列中取出元素
System.out.println("Consumed: " + item);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
在这个示例中:
由于 LinkedBlockingQueue 是线程安全的,因此生产者和消费者线程可以并发地访问队列,而不需要额外的同步控制。
LinkedBlockingQueue 提供了一个灵活的阻塞队列实现,既可以配置为有界队列也可以是无界队列,适用于需要高并发处理的生产者-消费者场景、任务调度和数据缓冲等应用。
PriorityBlockingQueue 是 Java java.util.concurrent 包中的一个优先级阻塞队列实现。它基于优先队列(Priority Queue)的数据结构,确保队列中元素的顺序按照自然顺序或者根据提供的 Comparator 来排序。以下是其主要实现原理:
业务应用场景
PriorityBlockingQueue 适用于以下业务场景:
应用代码案例
以下是一个使用 PriorityBlockingQueue 的示例,展示如何在任务调度系统中使用它。
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class PriorityBlockingQueueExample {
private static final BlockingQueue<Task> queue = new PriorityBlockingQueue<>(10, (t1, t2) -> {
// 优先级比较,优先级高的先执行
return t1.getPriority() - t2.getPriority();
});
static class Task implements Comparable<Task> {
private final int priority;
private final String name;
public Task(int priority, String name) {
this.priority = priority;
this.name = name;
}
public int getPriority() {
return priority;
}
@Override
public int compareTo(Task other) {
return other.priority - this.priority; // 优先级高的先执行
}
@Override
public String toString() {
return name;
}
}
public static void main(String[] args) {
// 添加任务
for (int i = 0; i < 15; i++) {
int priority = (int) (Math.random() * 10);
new Thread(() -> {
try {
queue.put(new Task(priority, "Task " + i));
System.out.println("Added: " + new Task(priority, "Task " + i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
// 执行任务
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
while (true) {
Task task = queue.take();
System.out.println("Executing: " + task);
Thread.sleep(1000); // 模拟任务执行时间
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
在这个示例中:
由于 PriorityBlockingQueue 是线程安全的,因此生产者和消费者线程可以并发地访问队列,而不需要额外的同步控制。
PriorityBlockingQueue 提供了一个高效的优先级阻塞队列实现,适用于需要按优先级顺序处理任务的业务场景。通过使用二叉堆来维护元素的顺序,它能够确保高优先级任务的优先执行。
除了以上7种并发容器,还有CopyOnWriteArrayList,来代替Vector、synchronizedList,CopyOnWriteArraySet来代替synchronizedSet,基于CopyOnWriteArrayList实现,ConcurrentSkipListSet可以代替synchronizedSortedSet,内部基于ConcurrentSkipListMap实现。兄弟们还有哪些应用场景和见解,欢迎评论留言,如果内容对你有些许帮助,V哥真诚向你求个赞,关注【威哥爱编程】一起成长。

V哥原创技术文章发布于 CSDN技术社区,欢迎关注【威哥爱编程】
一套让你觉对哇塞的JAVA视频教程!有趣有料有用
- END -

原文作者:Java架构栈;如有侵权,请联系管理员删除。