并发集合

概述

在多线程编程中,并发集合(Concurrent Collections)是用于在多个线程之间安全地共享数据的数据结构。Java 提供了多种并发集合类,这些类在 java.util.concurrent 包中。与传统的集合类(如 ArrayList、HashMap)不同,并发集合类在设计上考虑了线程安全性,因此可以在多线程环境中安全使用。

为什么需要并发集合?

在多线程环境中,如果多个线程同时访问和修改同一个集合,可能会导致数据不一致或程序崩溃。传统的集合类(如 ArrayList、HashMap)并不是线程安全的,因此需要使用同步机制(如 synchronized 关键字)来保证线程安全。然而,同步机制可能会导致性能问题,因为它会阻塞其他线程的访问。

并发集合通过内部实现线程安全的机制,避免了显式同步的需求,从而提高了性能和可读性。

常见的并发集合类

1. ConcurrentHashMap

ConcurrentHashMap 是 HashMap 的线程安全版本。它允许多个线程同时读取和写入数据,而不会出现数据不一致的问题。ConcurrentHashMap 使用了分段锁(Segmented Locking)机制,将整个 HashMap 分成多个段(Segment),每个段可以独立加锁,从而提高了并发性能。

代码示例

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

        // 多线程环境下添加元素
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                map.put("Key" + i, i);
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Map size: " + map.size());
    }
}

实践练习

  1. 修改上述代码,使用 HashMap 替换 ConcurrentHashMap,观察结果。
  2. 尝试在 ConcurrentHashMap 中使用 computeIfAbsent 方法,并解释其作用。

2. CopyOnWriteArrayList

CopyOnWriteArrayList 是 ArrayList 的线程安全版本。它通过在写操作时创建底层数组的新副本来实现线程安全。读操作不需要加锁,因此读操作非常高效。然而,写操作的性能较低,因为每次写操作都需要复制整个数组。

代码示例

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListExample {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

        // 多线程环境下添加元素
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                list.add("Element" + i);
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("List size: " + list.size());
    }
}

实践练习

  1. 修改上述代码,使用 ArrayList 替换 CopyOnWriteArrayList,观察结果。
  2. 尝试在 CopyOnWriteArrayList 中使用 iterator 方法,并解释其线程安全性。

3. BlockingQueue

BlockingQueue 是一个支持阻塞操作的队列接口。它常用于生产者-消费者模式中。BlockingQueue 的实现类(如 LinkedBlockingQueue、ArrayBlockingQueue)提供了线程安全的队列操作,包括阻塞的 put 和 take 方法。

代码示例

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class BlockingQueueExample {
    public static void main(String[] args) {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);

        // 生产者线程
        Runnable producer = () -> {
            try {
                for (int i = 0; i < 100; i++) {
                    queue.put("Message" + i);
                    System.out.println("Produced: Message" + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        // 消费者线程
        Runnable consumer = () -> {
            try {
                while (true) {
                    String message = queue.take();
                    System.out.println("Consumed: " + message);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread producerThread = new Thread(producer);
        Thread consumerThread = new Thread(consumer);

        producerThread.start();
        consumerThread.start();
    }
}

实践练习

  1. 修改上述代码,使用 ArrayBlockingQueue 替换 LinkedBlockingQueue,观察结果。
  2. 尝试在 BlockingQueue 中使用 offer 和 poll 方法,并解释其与 put 和 take 的区别。

总结

并发集合是多线程编程中的重要工具,它们提供了线程安全的集合操作,避免了显式同步的需求,从而提高了程序的性能和可读性。常见的并发集合类包括 ConcurrentHashMap、CopyOnWriteArrayList 和 BlockingQueue。通过学习和实践这些并发集合类,你将能够更好地处理多线程环境中的数据共享问题。

下一步

在掌握了并发集合的基本概念和使用方法后,你可以进一步学习 Java 中的其他并发工具,如 ExecutorService、ForkJoinPool 等,以及更高级的并发编程技术,如锁、条件变量等。