Java 我的代码是线程安全的吗?如果没有,如何在这种情况下实现线程安全?
我在面试中被问到一个问题。。 如果我想像这样在地图中存储值,该怎么办:Java 我的代码是线程安全的吗?如果没有,如何在这种情况下实现线程安全?,java,multithreading,arraylist,collections,thread-safety,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(); 这里,假设线程t1和t2并行运行。所以,如果列表中的我的意思是ConcurrentHashMap中的值是线程安全的吗?
key_1 --> value 1
key_2 --> value 2
key_1 --> value 3
这里,如果我们假设键和值都在字符串中,那么我们应该像这样获取映射
Map<String,List<String>> m1 = new HashMap();
这里,假设线程t1和t2并行运行。所以,如果
列表中的我的意思是ConcurrentHashMap中的值是线程安全的吗?如果是,那怎么办?如果没有,那么如何实现这一点呢???ConcurrentHashMap是线程安全的数据结构。如果在ConcurrentHashMap
的值中使用List
(它不是线程安全的),则List
不是线程安全的,因为两个线程可以保护List的引用,然后并行修改它
ConcurrentHashMap
是线程安全的,这意味着它的操作,如put
和putAll
e.t.c是线程安全的。这并不意味着你使用的数据结构作为它的值也成为线程安全的
如何做到这一点?
- 使用线程安全的
List
像CopyOnWriteArrayList
一样作为ConcurrentHashMap
中的值
- 使自定义
列表
并使其所有方法同步
- 通过传入集合,将非线程安全的
列表
转换为线程安全的列表
。synchronizedList(非线程安全列表)
李>
我说我们可以用ConcurrentHashMap来实现这个目的他们说可以
也许没关系,也许没关系。这取决于键1和键2之间是否存在特殊关系
假设一个线程为key_1存储一个新值,然后就在它可以为key_2存储一个相关值之前,它的时间片结束了。然后,另一个线程在第一个线程挂起时检查键1和键2的值。它看到键_1的新值,但看到键_2的旧值
在这一点上,第一个线程只更新了两个键的一半有关系吗
插入ConcurrentHashMap
将确保map数据结构本身在多线程应用程序中不会做任何有趣或错误的事情,但如果应用程序依赖于始终同时更新的这两个键,然后,您仍然需要某种显式锁定,以确保它们始终一起更新。通过代码进行测试很容易:
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());
Map m1=新的ConcurrentHashMap();
m1.put(“s1”,新的ArrayList());
CountDownLatch CountDownLatch=新的CountDownLatch(2);
新线程(()->{
int i=500;
而(i-->0){
m1.获取(“s1”).添加(i+);
}
countdownlock.countDown();
}).start();
新线程(()->{
int i=500;
而(i-->0){
m1.获取(“s1”).添加(i+);
}
countdownlock.countDown();
}).start();
倒计时闩锁。等待();
//可能小于1000,
//或ArrayIndexOutOfBoundsException在这行代码执行之前发生
System.out.println(m1.get(“s1”).size());
这取决于列表的实现。存储在ConcurrentHashMap中的值的线程安全性不高于或低于映射之外的值。@AndyTurner,请详细解释一下好吗?我没有理解你。对此很抱歉。你在地图上执行的操作将是线程安全的,即get()和put()以及containsKey()。但是,一旦获取了值[在您的示例中是一个列表],与特定列表元素相关的修改的线程安全性将完全取决于列表对象[或任何其他java对象]的实现。
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());