MST
星途 面试题库

面试题:Java中如何使一个非线程安全的Java Collections实现线程安全?

在Java中,许多集合类如ArrayList、HashMap等是非线程安全的。请描述至少两种方法使这些非线程安全的集合类在多线程环境下安全使用,并举例说明使用场景。
26.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

方法一:使用同步包装器

  1. 描述:Java提供了Collections类的静态方法来创建线程安全的集合包装器,例如Collections.synchronizedListCollections.synchronizedMap等。这些方法返回一个同步的集合,对该集合的所有操作都被同步,以确保线程安全。
  2. 示例
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SynchronizedListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        List<String> synchronizedList = Collections.synchronizedList(list);

        // 多线程操作synchronizedList
        Thread thread1 = new Thread(() -> {
            synchronizedList.add("element1");
        });
        Thread thread2 = new Thread(() -> {
            synchronizedList.add("element2");
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(synchronizedList);
    }
}
  1. 使用场景:适用于需要简单地将现有的非线程安全集合转换为线程安全集合,并且对性能要求不是特别高的场景。例如,在一些小型的多线程应用程序中,对集合的操作频率不是特别高的情况下可以使用。

方法二:使用并发集合类

  1. 描述:Java并发包(java.util.concurrent)提供了一些线程安全的集合类,如CopyOnWriteArrayListConcurrentHashMap等。这些集合类针对多线程环境进行了优化,通常具有更好的性能。
    • CopyOnWriteArrayList:在修改操作(如添加、删除元素)时,会创建一个底层数组的副本,在副本上进行修改,然后将新数组赋值给原数组。读操作则直接读取原数组,不需要加锁,因此读操作性能较高,但写操作相对较慢。
    • ConcurrentHashMap:采用分段锁机制,允许多个线程同时访问不同的段,提高了并发性能。
  2. 示例 - CopyOnWriteArrayList
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

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

        Thread thread1 = new Thread(() -> {
            list.add("element1");
        });
        Thread thread2 = new Thread(() -> {
            list.add("element2");
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}
  1. 示例 - ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap;

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

        Thread thread1 = new Thread(() -> {
            map.put("key1", 1);
        });
        Thread thread2 = new Thread(() -> {
            map.put("key2", 2);
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(map);
    }
}
  1. 使用场景
    • CopyOnWriteArrayList适用于读多写少的场景,例如事件监听列表,多个线程可能频繁读取事件,但很少添加或删除监听器。
    • ConcurrentHashMap适用于高并发读写的场景,如在大型的多线程Web应用程序中,需要频繁地读取和更新缓存数据的场景。