我的代码是线程安全的吗?如果没有,如何在其中实现线程安全?

时间:2019-04-25 10:25:24

标签: java multithreading arraylist collections thread-safety

面试中有人问我一个问题。 如果我想像这样在地图中存储值,该怎么办:

key_1 --> value 1
key_2 --> value 2
key_1 --> value 3

如果我们假设键和值都在字符串中,那么我们应该像这样获取地图。

Map<String,List<String>> m1 = new HashMap();

然后我被问到是否要实现线程安全?然后我说我们可以为此目的采用 ConcurrentHashMap ..他们说还可以。

然后他们问的值是我的意思是List是线程安全的? 例如

t1.get("key_1").put("value_4");    
t2.get("key_1").put("value_5");

在这里,假设线程t1和t2都并行运行。因此,如果我说List<String> ConcurrentHashMap 内部的值是线程安全的?如果是,那怎么办?如果否,那么如何实现?

3 个答案:

答案 0 :(得分:1)

ConcurrentHashMap是线程安全数据结构。如果您以List<String>的值使用ConcurrentHashMap(不是线程安全的),则List不是线程安全的,因为两个线程可以保护list的引用并随后对其进行并行修改。 / p>

ConcurrentHashMap是线程安全的,它的操作putputAll等是线程安全的。这并不意味着您使用的数据结构的值也将变为Thread。安全。

如何实现?

  • 使用List<String>之类的线程安全CopyOnWriteArrayList作为ConcurrentHashMap中的值。
  • 进行List的自定义,并使其所有方法Synchronized
  • 通过传入List将非线程安全List转换为线程安全Collections.synchronizedList(non-Thread safelist)

答案 1 :(得分:0)

  

我说我们可以为此目的使用ConcurrentHashMap ..他们说还可以。

也许还可以,也许不行。取决于key_1和key_2之间是否存在特殊关系。

假设一个线程为key_1存储了一个新值,然后在它可以为key_2存储一个相关值之前,它的时间片结束了。然后,在第一个线程挂起时,另一个线程检查key_1和key_2的值。它看到key_1的新值,但是看到key_2的 old 值。

在那一点上,第一个线程仅在更新两个密钥的过程中完成了吗?

插入ConcurrentHashMap可以确保地图数据结构本身在多线程应用程序中不会做任何有趣或错误的事情,但是,如果应用程序依赖于总是一起更新的那两个键,那么您就可以仍然需要某种显式锁定,以确保它们总是一起更新。

答案 2 :(得分:0)

通过代码测试很容易:

        Map<String,List<String>> m1 = new ConcurrentHashMap();
        m1.put("s1", new ArrayList<>());
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread(() -> {
            int i = 500;
            while (i-- > 0) {
                m1.get("s1").add(i + "");
            }
            countDownLatch.countDown();
        }).start();

        new Thread(() -> {
            int i = 500;
            while (i-- > 0) {
                m1.get("s1").add(i + "");
            }
            countDownLatch.countDown();
        }).start();
        countDownLatch.await();
        // may be < 1000,
        // or ArrayIndexOutOfBoundsException happens before this line of code executes
        System.out.println(m1.get("s1").size());