同步 Java 集合简介-Java快速入门教程
1. 概述
集合框架是Java的关键组件。它提供了大量的接口和实现,使我们能够以简单的方式创建和操作不同类型的集合。
尽管使用普通的不同步集合总体上很简单,但在多线程环境(也称为并发编程)中工作时,它也可能成为一个令人生畏且容易出错的过程。
因此,Java 平台通过在Collections类中实现的不同同步包装器为此方案提供了强大的支持。
通过这些包装器,可以通过多个静态工厂方法轻松创建所提供集合的同步视图。
在本教程中,我们将深入探讨这些静态同步包装器。此外,我们还将重点介绍同步集合和并发集合之间的区别。
2.syncdCollection方法
我们将在本综述中介绍的第一个同步包装器是syncdCollection() 方法。顾名思义,它返回由指定集合备份的线程安全集合。
现在,为了更清楚地了解如何使用此方法,让我们创建一个基本的单元测试:
Collection<Integer> syncCollection = Collections.synchronizedCollection(new ArrayList<>());
Runnable listOperations = () -> {
syncCollection.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));
};
Thread thread1 = new Thread(listOperations);
Thread thread2 = new Thread(listOperations);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
assertThat(syncCollection.size()).isEqualTo(12);
}
如上所示,使用此方法创建所提供集合的同步视图非常简单。
为了演示该方法实际上返回线程安全集合,我们首先创建几个线程。
之后,我们以 lambda 表达式的形式将一个Runnable实例注入到它们的构造函数中。请记住,Runnable是一个函数式接口,因此我们可以将其替换为 lambda 表达式。
最后,我们只检查每个线程是否有效地将六个元素添加到同步集合中,因此其最终大小为 12。
3.syncdList方法
同样,与syncdCollection() 方法类似,我们可以使用syncdList()包装器创建一个同步列表。
正如我们所料,该方法返回指定列表的线程安全视图:
List<Integer> syncList = Collections.synchronizedList(new ArrayList<>());
不出所料,syncdList() 方法的使用看起来与其更高级别对应的 synchronizedCollection() 几乎相同。
因此,正如我们在上一个单元测试中所做的那样,一旦我们创建了一个同步的 List,我们就可以生成多个线程。完成此操作后,我们将使用它们以线程安全的方式访问/操作目标列表。
此外,如果我们想迭代同步集合并防止意外结果,我们应该显式提供我们自己的线程安全循环实现。因此,我们可以使用同步块来实现这一点:
List<String> syncCollection = Collections.synchronizedList(Arrays.asList("a", "b", "c"));
List<String> uppercasedCollection = new ArrayList<>();
Runnable listOperations = () -> {
synchronized (syncCollection) {
syncCollection.forEach((e) -> {
uppercasedCollection.add(e.toUpperCase());
});
}
};
在我们需要迭代同步集合的所有情况下,我们应该实现这个习惯用法。这是因为同步集合上的迭代是通过对集合的多次调用来执行的。因此,它们需要作为单个原子操作执行。
同步块的使用确保了操作的原子性。
4.syncdMap方法
Collections类实现了另一个简洁的同步包装器,称为syncdMap()。我们可以使用它来轻松创建同步Map。
该方法返回提供的Map实现的线程安全:
Map<Integer, String> syncMap = Collections.synchronizedMap(new HashMap<>());
5.syncdSortedMap方法
还有一个syncdMap() 方法的对应实现。它被称为syncdSortedMap(),我们可以用它来创建一个同步的SortedMap实例:
Map<Integer, String> syncSortedMap = Collections.synchronizedSortedMap(new TreeMap<>());
6.syncdSet方法
接下来,在这篇文章中我们有syncdSet() 方法。顾名思义,它允许我们以最小的麻烦创建同步集。
包装器返回由指定Set 支持的线程安全集合:
Set<Integer> syncSet = Collections.synchronizedSet(new HashSet<>());
7.syncdSortedSet方法
最后,我们将在这里展示的最后一个同步包装器是syncdSortedSet()。
与到目前为止我们审查过的其他包装器实现类似,该方法返回给定SortedSet 的线程安全版本:
SortedSet<Integer> syncSortedSet = Collections.synchronizedSortedSet(new TreeSet<>());
8. ConcurrentHashMap和BlockingQueue
到目前为止,我们仔细研究了集合框架的同步包装器。
现在,让我们关注同步集合和并发集合之间的差异,例如ConcurrentHashMap和BlockingQueue实现。
8.1. 同步集合
同步集合通过内在锁定实现线程安全,并且整个集合都是锁定的。内在锁是通过封装集合的方法中的同步块实现的。
正如我们所期望的,同步集合可以确保多线程环境中的数据一致性/完整性。然而,它们可能会带来性能上的损失,因为一次只能有一个线程访问集合(也称为同步访问)。
有关如何使用同步方法和块的详细指南,请查看我们关于该主题的文章。
8.2. 并发集合
并发集合(例如ConcurrentHashMap)通过将其数据划分为段来实现线程安全。例如,在ConcurrentHashMap中,不同的线程可以在每个段上获取锁,因此多个线程可以同时访问Map(也称为并发访问)。
由于并发线程访问的固有优势,并发集合的性能比同步集合高得多。
因此,选择使用哪种类型的线程安全集合取决于每个用例的要求,应相应地对其进行评估。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix