1. 概述

集合框架是Java的关键组件。它提供了大量的接口和实现,使我们能够以简单的方式创建和操作不同类型的集合。

尽管使用普通的不同步集合总体上很简单,但在多线程环境(也称为并发编程)中工作时,它也可能成为一个令人生畏且容易出错的过程。

因此,Java 平台通过在Collections类中实现的不同同步包装器为此方案提供了强大的支持。

通过这些包装器,可以通过多个静态工厂方法轻松创建所提供集合的同步视图。

在本教程中,我们将深入探讨这些静态同步包装器。此外,我们还将重点介绍同步集合和并发集合之间的区别。

2.syncdCollection方法

我们将在本综述中介绍的第一个同步包装器是syncdCollection() 方法。顾名思义,它返回由指定集合备份的线程安全集合。

现在,为了更清楚地了解如何使用此方法,让我们创建一个基本的单元测试:

Collection 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);

}

Copy

如上所示,使用此方法创建所提供集合的同步视图非常简单。

为了演示该方法实际上返回线程安全集合,我们首先创建几个线程。

之后,我们以 lambda 表达式的形式将一个Runnable实例注入到它们的构造函数中。请记住,Runnable是一个函数式接口,因此我们可以将其替换为 lambda 表达式。

最后,我们只检查每个线程是否有效地将六个元素添加到同步集合中,因此其最终大小为 12。

3.syncdList方法

同样,与syncdCollection() 方法类似,我们可以使用syncdList()包装器创建一个同步列表。

正如我们所料,该方法返回指定列表的线程安全视图:

List syncList = Collections.synchronizedList(new ArrayList<>());Copy

不出所料,syncdList() 方法的使用看起来与其更高级别对应的 synchronizedCollection() 几乎相同。

因此,正如我们在上一个单元测试中所做的那样,一旦我们创建了一个同步的 List,我们就可以生成多个线程。完成此操作后,我们将使用它们以线程安全的方式访问/操作目标列表。

此外,如果我们想迭代同步集合并防止意外结果,我们应该显式提供我们自己的线程安全循环实现。因此,我们可以使用同步块来实现这一点:

List syncCollection = Collections.synchronizedList(Arrays.asList("a", "b", "c"));

List uppercasedCollection = new ArrayList<>();

Runnable listOperations = () -> {

synchronized (syncCollection) {

syncCollection.forEach((e) -> {

uppercasedCollection.add(e.toUpperCase());

});

}

};

Copy

在我们需要迭代同步集合的所有情况下,我们应该实现这个习惯用法。这是因为同步集合上的迭代是通过对集合的多次调用来执行的。因此,它们需要作为单个原子操作执行。

同步块的使用确保了操作的原子性。

4.syncdMap方法

Collections类实现了另一个简洁的同步包装器,称为syncdMap()。我们可以使用它来轻松创建同步Map。

该方法返回提供的Map实现的线程安全:

Map syncMap = Collections.synchronizedMap(new HashMap<>());

Copy

5.syncdSortedMap方法

 

还有一个syncdMap() 方法的对应实现。它被称为syncdSortedMap(),我们可以用它来创建一个同步的SortedMap实例:

Map syncSortedMap = Collections.synchronizedSortedMap(new TreeMap<>());

Copy

6.syncdSet方法

接下来,在这篇文章中我们有syncdSet() 方法。顾名思义,它允许我们以最小的麻烦创建同步集。

包装器返回由指定Set 支持的线程安全集合:

Set syncSet = Collections.synchronizedSet(new HashSet<>());

Copy

7.syncdSortedSet方法

最后,我们将在这里展示的最后一个同步包装器是syncdSortedSet()。

与到目前为止我们审查过的其他包装器实现类似,该方法返回给定SortedSet 的线程安全版本:

SortedSet syncSortedSet = Collections.synchronizedSortedSet(new TreeSet<>());

Copy

8. ConcurrentHashMap和BlockingQueue

到目前为止,我们仔细研究了集合框架的同步包装器。

现在,让我们关注同步集合和并发集合之间的差异,例如ConcurrentHashMap和BlockingQueue实现。

8.1. 同步集合

同步集合通过内在锁定实现线程安全,并且整个集合都是锁定的。内在锁是通过封装集合的方法中的同步块实现的。

正如我们所期望的,同步集合可以确保多线程环境中的数据一致性/完整性。然而,它们可能会带来性能上的损失,因为一次只能有一个线程访问集合(也称为同步访问)。

有关如何使用同步方法和块的详细指南,请查看我们关于该主题的文章。

8.2. 并发集合

并发集合(例如ConcurrentHashMap)通过将其数据划分为段来实现线程安全。例如,在ConcurrentHashMap中,不同的线程可以在每个段上获取锁,因此多个线程可以同时访问Map(也称为并发访问)。

由于并发线程访问的固有优势,并发集合的性能比同步集合高得多。

因此,选择使用哪种类型的线程安全集合取决于每个用例的要求,应相应地对其进行评估。

相关阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。