【深入Java基础】HashMap的高级用法(二):同步
HashMap的高级用法:同步
HashMap是不支持同步的,不能用于多线程中。而HashTable是同步的,这个日后再论。我们可以使用以下两种方法来实现HashMap的同步:
-
使用
ConcurrentHashMap
-
使用
Collections.synchronizedMap
获取同步map
使用ConcurrentHashMap
ConcurrentHashMap<Integer,String> concurrentHashMap = new ConcurrentHashMap<>();
使用Collections.synchronizedMap获取同步map
HashMap<Integer,String> hashMap = new HashMap<>();
Map<Integer,String> synchronizedMap = Collections.synchronizedMap(hashMap);
测试
对于没有同步的HashMap在多线程中运行一定会抛出异常:
java.util.ConcurrentModificationException
运行两个线程同时对一个hashmap进行读写操作:
HashMap<Integer,String> hashMap1 = new HashMap<>();
//写线程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
hashMap1.put(i,"value"+i);
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//读线程
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (hashMap1.size() > 0) {
for (Map.Entry entry : hashMap1.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
运行以上代码抛出异常java.util.ConcurrentModificationException
。原因是未实现同步时,在迭代hashmap的时候不能修改数据。
Collections.synchronizedMap实现同步:
HashMap<Integer,String> hashMap1 = new HashMap<>();
Map<Integer, String> map = Collections.synchronizedMap(hashMap1);
//写线程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
map.put(i,"value"+i);
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//读线程
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (map.size() > 0) {
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
运行以上代码任然抛出异常java.util.ConcurrentModificationException
。可见该方法并不安全。(还是这样测试有问题?)
ConcurrentHashMap实现同步:
ConcurrentHashMap<Integer,String> concurrentHashMap = new ConcurrentHashMap<>();
//写线程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
concurrentHashMap.put(i,"value"+i);
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//读线程
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (concurrentHashMap.size() > 0) {
for (Map.Entry entry : concurrentHashMap.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
运行以上代码,无异常。
所以为了保证多线程下的hashmap的数据一致性使用ConcurrentHashMap最为合理。并且ConcurrentHashMap的效率也比较高。为什么日后再论。
使用lock锁手动实现hashmap的同步:
ReentrantLock lock = new ReentrantLock(true);
HashMap<Integer, String> hashMap1 = new HashMap<>();
//写线程
new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 1000; i++) {
hashMap1.put(i, "value" + i);
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
} finally {
//lock.unlock();
}
}
}).start();
//读线程
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
lock.lock();
try {
if (hashMap1.size() > 0) {
for (Map.Entry entry : hashMap1.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
lock.unlock();
}
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();