浅谈Java中HashMap的线程安全问题
前言
关于HashMap的线程安全问题,经常出现在面试题中。既然面试者都这么关心HashMap的线程安全问题,为什么在平时的开发当中不废弃HashMap的使用呢?HashMap的线程安全问题到底需不需要我们注意呢?
什么是线程安全?
线程安全就是说在多线程环境下,不同的线程对共享数据的访问和修改问题,要保证多个线程对共享数据的并发访问不会出现冲突,从而确保程序的正确性和可靠性。(就是大家别乱来,排好队一个一个的来)。
打个比方:小明在教室写作业,中途去上了厕所,这时课代表小红来收作业,就把小明没写完的作业收了上去。这里小明和课代表就是两个线程,作业就是共享数据,因为两个线程没有沟通或者小明没有对没写完的作业进行保护,导致老师看到的结果不是自己想要的结果。
Java程序中会出现线程安全问题的地方在哪?
-
共享变量:通常是定义在方法外部、类内部的变量,可以被多个线程同时访问和修改。共享变量包括类级别的静态变量和实例级别的成员变量。
-
静态变量:静态变量属于类级别的变量(当类被加载时,静态变量就已经被初始化),在多个线程中共享。如果多个线程同时访问和修改同一个静态变量,就可能会出现线程安全问题。为了解决这个问题,可以使用同步机制来控制多个线程对静态变量的访问;
-
单例模式:单例模式是一种常用的设计模式,它保证在整个应用程序中只有一个实例对象。如果多个线程同时访问和修改单例对象的属性,就可能会出现线程安全问题。为了解决这个问题,可以使用同步机制来控制多个线程对单例对象的访问。
-
缓存:在多线程环境下,如果多个线程同时访问和修改同一个缓存对象,就可能会出现线程安全问题。为了解决这个问题,可以使用同步机制来控制多个线程对缓存对象的访问。
-
其他:多个线程同时操作同一个文件或网络连接、多个线程同时执行数据库操作等。在这些情况下,也需要采取相应的线程安全措施来保证程序的正确性和可靠性。
使用HashMap到底安不安全呢?
对于这个问题,大家有没有注意过,或者思考过这么一个问题:我们在平时开发中,我们都在使用HashMap,但是为什么没有出现问题呢?
因为大多数情况下,我们都是在方法中去new HashMap<>()
,即使在多线程环境下,当每个线程来执行这个方法时,都会重新new出自己的map实例,不纯在共享的问题。(此处有异议的欢迎评论指正)
HashMap在什么情况才会出现线程安全问题?
使用HashMap出现线程安全的情况,就是将一个HashMap的实例传递到不同的线程去进行操作,举个例子:
public class SharedHashMap {
private final Map<String, String> map = new HashMap<>();
public void put(String key, String value) {
map.put(key, value);
}
public String get(String key) {
return map.get(key);
}
}
在这个例子中,我们定义了一个SharedHashMap
类,该类维护一个HashMap
实例,并提供了put
和get
方法来进行写入和读取操作。
当我们用多个线程对该实例进行put
、get
操作时,就有可能出现数据不一致的问题:
SharedHashMap sharedHashMap = new SharedHashMap();
// 线程1
new Thread(() -> {
sharedHashMap.put("key1", "value1");
}).start();
// 线程2
new Thread(() -> {
String value = sharedHashMap.get("key1");
// ...
}).start();
在这里我们为了实现线程安全,可以使用synchronized
关键字来修饰put
和get
方法,以保证在多个线程同时访问该HashMap
实例时,每次只有一个线程能够访问它。
public class SharedHashMap {
private final Map<String, String> map = new HashMap<>();
public synchronized void put(String key, String value) {
map.put(key, value);
}
public synchronized String get(String key) {
return map.get(key);
}
}
总结
综上,我们可以知道,即使我们都在大面积的使用HashMap但却很少出现线程安全问题的原因就在于我们大多情况下都是在方法中实例化HashMap。如果出现在类中实例化了的HashMap也应知道如何处理,或者直接使用ConcurrentHashMap。对于HashMap不是知道它是线程不安全的就可以了,我们还要结合我们实际的生产环境去分析它是否会产生线程安全问题。(以上分析有异议的地方欢迎大佬们指正,感谢!)