我们知道HashMap是非线程安全的类,那线程安全和非线程安全使用时实际效果到底有什么区别呢?我们可以通过一个代码片段来实际测试验证一下。测试方法:使用四个线程并发地分别向map中放入1000个数据项,然后检查最终的size是否为4000来验证线程安全与否。
map size最终结果会是多少呢?
import java.util.*;
import java.util.concurrent.CountDownLatch;
public class MyMapTest {
public static void main(String[] args) {
Map<Integer, Integer> map = new HashMap<>();
CountDownLatch countDownLatch = new CountDownLatch(4);
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
map.put(i, i);
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 1000; i < 2000; i++) {
map.put(i, i);
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 2000; i < 3000; i++) {
map.put(i, i);
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 3000; i < 4000; i++) {
map.put(i, i);
}
countDownLatch.countDown();
}).start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//map size将小于4000
System.out.println("map size:" + map.size());
}
}
上面代码实际运行起来后会发现size小于4000,而且小很多,可能只有2000多。
如何做到map size 一定为4000呢?
- 使用
Collections.synchronizedMap(map),包装成同步Map,原理就是在HashMap的所有方法上synchronized
Map<Integer, Integer> map = Collections.synchronizedMap(new HashMap<>());
- 将HashMap换成线程安全的类如:
ConcurrentHashMap。
Map<Integer, Integer> map = new ConcurrentHashMap<>();
- map.put() 时做同步操作。
import java.util.*;
import java.util.concurrent.CountDownLatch;
public class MyMapTest {
public static void main(String[] args) {
Map<Integer, Integer> map = new HashMap<>();
CountDownLatch countDownLatch = new CountDownLatch(4);
//对象锁
final Object object = new Object();
new Thread(() -> {
synchronized (object) {
for (int i = 0; i < 1000; i++) {
map.put(i, i);
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
synchronized (object) {
for (int i = 1000; i < 2000; i++) {
map.put(i, i);
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
synchronized (object) {
for (int i = 2000; i < 3000; i++) {
map.put(i, i);
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
synchronized (object) {
for (int i = 3000; i < 4000; i++) {
map.put(i, i);
}
}
countDownLatch.countDown();
}).start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//这里map size将一定等于4000
System.out.println("map size:" + map.size());
}
}
HashMap一定是非线程安全的吗?
其实,如果一开始只有一个线程往HashMap中存放数据,后续有多个读线程从HashMap中取数据,而不会再写入新的数据,是不会有线程安全的问题的。读操作只是读取数据,不会对内部共享变量做更改,相当于这个map是不可变的,所以不会有线程安全问题。