標籤:
1.前幾天修改一個bug的時候發現一個Java資料結果並發的問題。大致過程如下:
其中Bean的資料結果如下,其中包含一個Map,主要是為了記錄使用者的使用次數。
public class Bean { private Map<String,String> map = new HashMap<String,String>(); private String userId; private int count = 0; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + count; result = prime * result + ((map == null) ? 0 : map.hashCode()); result = prime * result + ((userId == null) ? 0 : userId.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Bean other = (Bean) obj; if (count != other.count) return false; if (map == null) { if (other.map != null) return false; } else if (!map.equals(other.map)) return false; if (userId == null) { if (other.userId != null) return false; } else if (!userId.equals(other.userId)) return false; return true; } @Override public String toString() { return "Bean [map=" + map + ", userId=" + userId + ", count=" + count + "]"; } public Map<String, String> getMap() { return map; } public void setMap(Map<String, String> map) { this.map = map; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub }}
Runnable:
public class TaskRunnable implements Runnable{ private int operationNumber; public TaskRunnable(int a){ this.operationNumber = a; } @Override public void run() { Action action = new Action(); Bean bean = action.getAndUpdateBeanFromCache(); System.out.println("threadId is id = " + Thread.currentThread().getId()); if(bean==null){ bean = new Bean(); bean.setUserId("12344"); bean.setCount(11); } Map<String,String> map = bean.getMap(); map.put(operationNumber+"",operationNumber+""); System.out.println("map key = " + operationNumber + " ," + " value = " + operationNumber); RedisUtil.setCache(RedisConstant.testKey, new Gson().toJson(bean)); }}
Action:
public class Action { public Bean getAndUpdateBeanFromCache(){ Bean bean = new Bean(); String key = RedisConstant.testKey; String value = RedisUtil.getCache(key); Type type = new TypeToken<Bean>(){}.getType(); bean = new Gson().fromJson(value,type); return bean; }}
MainClass:
public class MainClass { public static void main(String[] args) throws InterruptedException { for(int i = 0;i<100;i++){ Thread t = new Thread(new TaskRunnable(i)); //啟動一百個線程測試 t.start(); } }}
出現的問題,啟動的一百個線程中並不是沒有count都被記錄下來,主要原因是因為HashMap這種資料結構在並發的時候存在一定的問題,但是如何解決這個問題,最後採用了Redis Hash Map的資料結構記錄了使用者的使用,經測試不會出現並發問題。主要原因是Redis是個單線程啟動並執行程式,其中HashMap並不會出現這個並發的問題。
2.曾憲傑 大型網站系統與Java中介軟體實踐
在這個本書中舉出一個例子,跟這個很相似,如下:
public class TestClass { private HashMap<String,Integer> map = new HashMap<String,Integer>(); public synchronized void add(String key){ Integer value = map.get(key); if(value==null){ map.put(key, 1); }else{ map.put(key, value+1); } }}
這個方法雖然能夠正確的計數,但是在高並發的時候,卻十分的影響效能,效率不高。將Map換成ConcurrentHashMap這個結構,代碼如下:
public class TestClass { private ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<String,Integer>(); public void add(String key){ Integer value = map.get(key); if(value==null){ map.put(key, 1); }else{ map.put(key, value+1); } }}
這樣的寫法顯然會造成高並發的線程的問題。
—-路漫漫其修遠兮,吾將上下而求索!
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
HashMap&&Redis Concurrent Problem