Java 中并发集合的必要性
Java 中的并发集合
Java 是一种支持并发和多线程的著名计算机语言。开发人员可以使用同步关键字来保证线程之间的正确同步。此外,Java 的集合框架还提供了各种可用于保存和操作数据的集合。
程序员可以使用 synchronized 关键字使这些集合线程安全。涉及多个线程并发操作的程序的高效和安全执行取决于此功能。
Java 中并发集合的必要性是什么?
ArrayList、LinkedList、HashSet、HashMap 和 LinkedHashMap 只是 Java 集合结构中的几个类,它们不打算在多线程环境中安全使用。
这意味着如果我们在多个线程同时访问这些集合的情况下使用这些集合,结果需要更准确。为了解决这个问题,我们需要线程安全的类,多个线程可以访问这些类而不会造成任何问题。
虽然我们可以使用 Collections.synchronizedList(list) 等方法同步这些集合,但这不是最好的解决方案。
即使其他线程只需要读取数据,当我们使用此方法时,只有一个线程可以一次访问整个列表。这可能会降低机制的工作效率。我们必须使用并发集合来更有效地管理并发访问。创建这些集合是为了让众多线程可以同时访问它们而不会出现问题。
ConcurrentHashMap
ConcurrentHashMap 是 Java 5 中广泛使用的集合类。它之所以流行,是因为它为 Hashtable 或 Synchronized Map 类提供了并发选项,通过使用细粒度锁定允许更高级别的并发性。
使用 ConcurrentHashMap,许多读者可以同时访问 Map,而 Map 的一部分将被锁定以进行写入操作,具体取决于 Map 的并发级别。这有助于提供比同步映射更好的可扩展性。
算法
步骤 1 - 导入 java.util.concurrent.* 包。它包含并发实用程序的类。
步骤 2 - 使用 main 方法定义一个名为 MyConcurrentHashMap 的类。这是程序的入口点。
步骤 3 - 创建一个名为"concurrentHashMap"的 ConcurrentHashMap 类的新实例。
步骤 4 - 使用 put 方法向 parallelHashMap 添加三个键值对。每个键值对代表一个产品 ID 及其对应的产品名称。
步骤 5 - 使用 println 方法将 parallelHashMap 的内容打印到控制台。这将以无序方式输出 parallelHashMap 的内容。
示例 1
此程序演示如何在 Java 中使用 ConcurrentHashMap 类。
import java.util.concurrent.*; public class MyConcurrentHashMap { public static void main(String[] args){ ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<>(); concurrentHashMap.put("P001", "Apple Iphone"); concurrentHashMap.put("P002", "Samsung Smartphone"); concurrentHashMap.put("P003", "Google Pixel"); System.out.println(concurrentHashMap); } }
输出
{P001=Apple Iphone, P003=Google Pixel, P002=Samsung Smartphone}
BlockingQueue
Java 5 中一个流行的集合类是 BlockingQueue,它有助于实现生产者-消费者设计模式。它使用简单,并且对 put() 和 take() 函数具有内置的阻塞支持。
如果队列已满,put() 方法将等待;如果队列为空,take() 方法将等待。因此,控制生产者和客户之间的材料移动更加简单。
ArrayBlockingQueue 和 LinkedBlockingQueue 是 Java 5 中可用的两个不同版本的 BlockingQueue。它们都使用先进先出 (FIFO) 元素排列。 LinkedBlockingQueue 可能存在边界,而 ArrayBlockingQueue 则存在边界,并由数组支持。
CopyOnWriteArrayList
CopyOnWriteArrayList 是一种数据结构,每次有更新操作时,都会创建底层 ArrayList 的克隆副本。JVM 会自动将此克隆副本与原始 ArrayList 同步。因此,执行读取操作的线程不会受到影响。
但是,这种方法可能很昂贵,因为每次更新操作都会创建一个克隆副本。因此,如果频繁的操作是读取操作,CopyOnWriteArrayList 是最佳选择。此数据结构是 ArrayList 的线程安全版本,允许插入重复项、空值和异构对象,同时保留它们的顺序。
关于 CopyOnWriteArrayList 需要注意的一件重要事情是,其 Iterator 无法执行删除操作,并且在迭代器上调用 add() 和 set() 方法将导致 UnsupportedOperationException 运行时异常。此外,CopyOnWriteArrayList 的迭代器永远不会抛出 ConcurrentModificationException。
算法
步骤 1 − 程序将三个组件添加到属于 CopyOnWriteArrayList 类的名为"l"的对象中。即使主线程继续循环遍历列表,也会启动一个新线程来向其添加新元素。
步骤 2 - 为了保护迭代过程免受子线程对列表进行的任何更改的影响,软件依赖于 CopyOnWriteArrayList 类。
步骤 3 - 打印列表中的每个元素(包括子线程添加的"Z"元素)后,按顺序打印完整列表。
示例 2
以下程序演示了如何使用 CopyOnWriteArrayList 类来确保在迭代列表时线程安全。
import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; public class ConcurrentDemo extends Thread { static CopyOnWriteArrayList<String> l = new CopyOnWriteArrayList<String>(); public void run(){ // Child thread trying to // add new element in the // Collection object l.add("Z"); } public static void main(String[] args) throws InterruptedException{ l.add("W"); l.add("X"); l.add("Y"); // We create a child thread // that is going to modify // ArrayList l. ConcurrentDemo t = new ConcurrentDemo(); t.start(); Thread.sleep(1000); // Now we iterate through // the ArrayList and get // exception. Iterator itr = l.iterator(); while (itr.hasNext()) { String s = (String)itr.next(); System.out.println(s); Thread.sleep(1000); } System.out.println(l); } }
输出
W X Y Z [W, X, Y, Z]
结论
在 Java 中使用集合进行多线程操作时,使用线程安全集合(如 ConcurrentHashMap、BlockingQueue 和 CopyOnWriteArrayList)对于避免数据不一致和提高程序效率至关重要。这些集合提供各种功能以满足不同的并发需求,并可提高涉及多线程的 Java 应用程序的性能。