【深入Java基礎】HashMap的高階用法(二):同步
阿新 • • 發佈:2019-01-29
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();