0
点赞
收藏
分享

微信扫一扫

Java(day127):使用`Collections.synchronizedList()`,`synchronizedMap()`等方法实现集合的线程安全

前言

在Java中,当多个线程同时访问一个集合并进行修改时,可能会出现线程安全问题。为了避免这种情况,Java提供了一些线程安全的集合类以及方法来保证集合的同步访问。

Java 通过 Collections.synchronizedList()synchronizedMap()synchronizedSet() 等方法,使得普通的集合类变为线程安全。它们通过内部加锁机制来保证同一时刻只有一个线程能够访问集合。

这些方法本质上是为给定的集合增加了同步锁,使得集合操作变得线程安全。在大多数情况下,它们会为每个方法调用加锁(synchronized),从而保证线程之间的安全操作。

1. Collections.synchronizedList()

Collections.synchronizedList() 方法将普通的 List 转换成线程安全的 List。这个线程安全的 List 会通过对每个操作加锁来避免多线程间的竞争条件。

使用示例:

import java.util.*;

public class SynchronizedListExample {
public static void main(String[] args) {
// 创建一个普通的ArrayList
List<String> list = new ArrayList<>();

// 使用Collections.synchronizedList将其转化为线程安全的List
List<String> synchronizedList = Collections.synchronizedList(list);

// 多线程操作同步List
synchronizedList.add(Java);
synchronizedList.add(Python);

// 使用synchronizedList的集合操作
synchronized (synchronizedList) { // 显式加锁
for (String language : synchronizedList) {
System.out.println(language);
}
}
}
}

输出结果:

Java
Python
  • 在这个示例中,我们创建了一个普通的 ArrayList,并通过 Collections.synchronizedList() 将其转化为线程安全的 List
  • 由于 synchronizedList 是线程安全的,因此多个线程可以安全地访问它。
  • 需要注意的是,synchronizedList() 不会在遍历时自动加锁,所以在迭代时显式加锁,确保线程安全。

2. Collections.synchronizedMap()

Collections.synchronizedMap() 方法将普通的 Map 转换为线程安全的 Map。通过为每个操作加锁,synchronizedMap() 保证了多线程情况下对 Map 的访问是安全的。

使用示例:

import java.util.*;

public class SynchronizedMapExample {
public static void main(String[] args) {
// 创建一个普通的HashMap
Map<String, String> map = new HashMap<>();

// 使用Collections.synchronizedMap将其转化为线程安全的Map
Map<String, String> synchronizedMap = Collections.synchronizedMap(map);

// 向线程安全的Map中添加键值对
synchronizedMap.put(Java, Programming);
synchronizedMap.put(Python, Scripting);

// 使用synchronizedMap的集合操作
synchronized (synchronizedMap) { // 显式加锁
for (Map.Entry<String, String> entry : synchronizedMap.entrySet()) {
System.out.println(entry.getKey() + = + entry.getValue());
}
}
}
}

输出结果:

Java = Programming
Python = Scripting
  • 在这个示例中,我们通过 Collections.synchronizedMap() 将普通的 HashMap 转换为线程安全的 Map
  • 需要注意的是,和 synchronizedList() 类似,synchronizedMap() 在遍历时没有自动加锁,因此也需要显式加锁,以保证在多线程环境下的线程安全。

3. Collections.synchronizedSet()

Collections.synchronizedSet() 方法与前两者类似,它将一个普通的 Set 转换为线程安全的 Set。通过为所有操作加锁来保证线程安全。

使用示例:

import java.util.*;

public class SynchronizedSetExample {
public static void main(String[] args) {
// 创建一个普通的HashSet
Set<String> set = new HashSet<>();

// 使用Collections.synchronizedSet将其转化为线程安全的Set
Set<String> synchronizedSet = Collections.synchronizedSet(set);

// 向线程安全的Set中添加元素
synchronizedSet.add(Java);
synchronizedSet.add(Python);

// 使用synchronizedSet的集合操作
synchronized (synchronizedSet) { // 显式加锁
for (String language : synchronizedSet) {
System.out.println(language);
}
}
}
}

输出结果:

Java
Python
  • 在这个示例中,我们通过 Collections.synchronizedSet() 将普通的 HashSet 转换为线程安全的 Set
  • 如同 ListMap,我们需要显式加锁来确保遍历操作的线程安全。

4. 总结:Collections.synchronized*() 方法

方法 说明 用途
synchronizedList(List<T> list) 返回一个线程安全的 List 实现 用于将普通的 List 转换为线程安全的 List
synchronizedMap(Map<K,V> map) 返回一个线程安全的 Map 实现 用于将普通的 Map 转换为线程安全的 Map
synchronizedSet(Set<E> set) 返回一个线程安全的 Set 实现 用于将普通的 Set 转换为线程安全的 Set
  • 线程安全性:这些方法通过内部的同步机制保证了集合的线程安全,确保多个线程可以安全地同时访问和修改集合。
  • 显式加锁:虽然这些方法将集合本身转换为线程安全,但在对集合进行迭代等操作时,我们仍然需要显式地加锁。这是因为这些集合内部并不自动加锁。
  • 适用场景:适用于那些需要保证线程安全,但集合操作频率较低或不涉及频繁的迭代操作的场景。

5. 限制和注意事项

  • 性能:使用这些同步集合方法时,由于所有对集合的操作都需要加锁,可能会引入性能瓶颈。尤其在多个线程频繁访问集合时,性能会受到影响。
  • 写操作和迭代操作:对于涉及修改和迭代的操作,特别是在多线程环境下,synchronized会导致性能下降。因此,如果读操作多,写操作少,使用 CopyOnWriteArrayListCopyOnWriteArraySet 等其他线程安全集合类可能会更合适。
  • 死锁风险:如果多个线程之间存在复杂的依赖关系,使用同步方法时要小心死锁的发生。

6. 选择合适的同步方法

  • 使用Collections.synchronized*()方法:当你有现有的集合并希望使它们线程安全时,可以使用这些同步方法。它们适用于那些对性能要求不高、集合访问相对较少的场景。
  • 使用CopyOnWrite系列类:对于读多写少的场景,CopyOnWriteArrayListCopyOnWriteArraySet提供了更好的性能,因为它们不会对读取操作加锁,而是通过复制整个集合来处理写操作。

通过理解这些同步集合类的优缺点,可以在开发过程中根据具体的需求选择合适的集合类,以确保程序的线程安全性并优化性能。

举报

相关推荐

线程安全的集合

0 条评论