HashMap&&Redis Concurrent Problem

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);
        }
    }
}

這樣的寫法顯然會造成高併發的線程的問題。
—-路漫漫其修遠兮,吾將上下而求索!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章