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

这样的写法显然会造成高并发的线程的问题。

—-路漫漫其修远兮,吾将上下而求索!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-07-29 20:11:03

HashMap&&Redis Concurrent Problem的相关文章

Exchanger, Changing data between concurrent tasks

The Java concurrency API provides a synchronization utility that allows the interchange of data between two concurrent tasks. In more detail, the Exchanger class allows the definition of a synchronization point between two threads. When the two threa

Jedis连接Redis三种模式

这里说的三种工作模式是指: 1.单机模式 2.分片模式 3.集群模式(since 3.0) 说明图详见以下: 使用单机模式连接: 1 private String addr="192.168.1.1"; 2 private String port="6236"; 3 private String key="key"; 4 private Jedis jedis=new Jedis(addr,port);//Jedis获取到的Redis数据在jed

LogStash启动报错:&lt;Redis::CommandError: ERR unknown command &#39;script&#39;&gt;与batch_count 的 配置

环境条件: 系统版本:centos 6.8 logstash版本:6.3.2 redis版本:2.4 logstash  input配置: input { redis { host => "172.16.73.33" #redis ip port => "52611" #redis 端口 password =>"123456" #redis 密码 db => 9 # 指定redis 库编号 data_type =>

面试题:各大公司Java后端开发面试题总结 !=!未看

ThreadLocal(线程变量副本) Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量. 采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突. ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本. ThreadLocal在Spring中发挥着巨大的作用,在管理Request作用域中的Bean.事

各大公司 Java 后端开发面试题总结

ThreadLocal(线程变量副本) Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量. 采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突. ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本. ThreadLocal在Spring中发挥着巨大的作用,在管理Request作用域中的Bean.事

[LeetCode#159] Missing Ranges Strobogrammatic Number

Problem: Given a string, find the length of the longest substring T that contains at most 2 distinct characters. For example, Given s = “eceba”, T is "ece" which its length is 3. Analysis: This is a very very typical question in using slide wind

Java修炼之道--集合框架

原作地址:https://github.com/frank-lam/2019_campus_apply 前言 Java集合框架 (Java Collections Framework, JCF) 也称容器,这里可以类比 C++ 中的 STL,在市面上似乎还没能找到一本详细介绍的书籍.在这里主要对如下部分进行源码分析,及在面试中常见的问题. 例如,在阿里面试常问到的 HashMap 和 ConcurrentHashMap 原理等等.深入源码分析是面试中必备的技能,通过本文的阅读会对集合框架有更深一

各大公司Java面试题超详细总结

ThreadLocal(线程变量副本)Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量.采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突.ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本.ThreadLocal在Spring中发挥着巨大的作用,在管理Request作用域中的Bean.事务管理.

各大公司Java后端面试题,网友回复:不费洪荒之力面试成功

ThreadLocal(线程变量副本) Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量. 采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突. ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本. ThreadLocal在Spring中发挥着巨大的作用,在管理Request作用域中的Bean.事