在Java中,线程安全的集合类通过多种机制确保在多线程环境下操作集合时不会发生数据竞争或不一致的情况。下面是一些常见的线程安全集合类及其实现线程安全的方式:
1. Vector
和 Hashtable
Vector
和Hashtable
是早期版本的Java中的线程安全集合类。- 它们通过在每个方法上使用
synchronized
关键字来实现线程安全。- 这意味着每个方法在执行时都会获得对象的锁,从而保证同一时间只有一个线程可以访问这些方法。
- 虽然这种方法保证了线程安全,但在高并发情况下会导致较高的锁竞争,进而可能导致性能瓶颈。
Vector<Integer> vector = new Vector<>();
vector.add(1);
vector.add(2);
Hashtable<String, String> hashtable = new Hashtable<>();
hashtable.put("key", "value");
2. Collections.synchronizedList
和 Collections.synchronizedMap等
- Java提供了
Collections
类中的静态方法来创建线程安全的集合。- 这些方法返回的集合通过内部锁机制确保线程安全。
- 这些方法也通过在每个方法上使用
synchronized
关键字来实现线程安全,与Vector
和Hashtable
类似。
List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>());
synchronizedList.add(1);
synchronizedList.add(2);
Map<String, String> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
synchronizedMap.put("key", "value");
// jdk 17 源码
public void add(int index, E element) {
synchronized (mutex) {
list.add(index, element);
}
}
3. CopyOnWriteArrayList
和 CopyOnWriteArraySet
CopyOnWriteArrayList
和CopyOnWriteArraySet
是通过“写时复制”机制实现线程安全的。- 当发生写操作时(如添加或删除元素),它们会创建一个新的底层数组,并在新数组上执行修改操作。读操作则不需要加锁,因此在读多写少的场景下性能较高。
CopyOnWriteArrayList<Integer> cowList = new CopyOnWriteArrayList<>();
cowList.add(1);
cowList.add(2);
CopyOnWriteArraySet<Integer> cowSet = new CopyOnWriteArraySet<>();
cowSet.add(1);
cowSet.add(2);
// jdk 17 源码:
public boolean add(E e) {
synchronized (lock) {
Object[] es = getArray();
int len = es.length;
es = Arrays.copyOf(es, len + 1);
es[len] = e;
setArray(es);
return true;
}
}
4. ConcurrentHashMap
ConcurrentHashMap
是一个高效的线程安全Map实现。- 它通过分段锁(Segmented Locking)机制来实现线程安全,
- 即将整个Map分为多个段(Segment),每个段有自己的锁。
- 这样,在高并发情况下,不同的线程可以并发地访问不同的段,从而提高并发性能。
ConcurrentHashMap<String, String> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key", "value");
5. BlockingQueue
- Java的并发包(
java.util.concurrent
)提供了多种阻塞队列,- 如
ArrayBlockingQueue
、LinkedBlockingQueue
和PriorityBlockingQueue
。- 这些队列在插入和移除元素时使用内部锁来确保线程安全,并提供了线程间通信的机制。
BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(10);
blockingQueue.put(1);
blockingQueue.take();
阻塞队列的工作原理主要依赖于以下两个方面:
- 内部锁(Intrinsic Lock):用于确保对队列的操作是线程安全的。
- 条件变量(Condition Variables):用于管理线程的等待和唤醒。