• 记定位一次多线程数据安全问题


    背景:

    项目中有一个疑难用户入库task,该task是全国范围共用的,事件单是湖北提出的,说是入库的数据出现了重复的,一开始就按照特殊省份对待定位,结果跑偏了

    问题很简单,但是很难发现:现帖出问题代码

    TaskEngine是线程池工具类,这里是异步调用入库接口,数据封装在inputObject中,所有线程共享inputObject,由于入库逻辑较为复杂,所以比较耗时,多次异步调用,每次循环只

    会覆盖掉inputObject中封装的数据,所以如果接口很慢的时候,就会出现最后一个线程中的数据覆盖掉前面线程中的数据,导致出现同一数据出现多次入库。

    为更直观的解释上面问题,请看如下例子。

    1.线程类

    import java.util.Map;
    
    /**
     * @ClassName TestThread
     * @Description screenShot 梦想家
     * @Author Zhai XiaoTao https://www.cnblogs.com/zhaiyt
     * @Date 2019/1/21 17:12
     * @Version 1.0
     */
    public class TestThread implements Runnable {
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
    
        private Map<String, Object> param;
    
        public TestThread(Map param) {
            this.param = param;
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.param);
        }
    }
    

    2.测试类

    package com.zyt.creenshot.service.impl;
    
    import com.zyt.creenshot.service.thread.TestThread;
    import com.zyt.creenshot.util.TaskEngine;
    import org.springframework.stereotype.Service;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @ClassName TestServiceImpl
     * @Description screenShot 梦想家
     * @Author Zhai XiaoTao https://www.cnblogs.com/zhaiyt
     * @Date 2019/1/15 17:34
     * @Version 1.0
     */
    @Service
    public class TestServiceImpl{
    
        public static void main(String[] args) {
            testTask();
        }
    
        public static void testTask(){
            List<String> list = new ArrayList<>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            list.add("6");
            list.add("7");
            list.add("8");
            list.add("9");
            Map<String,Object> param = new HashMap<>();
    
            for (int i = 0 ;i <3 ; i++) {
                List<String> sublist = list.subList(i * 3, 3*(i+1));
                param.put("subList",sublist);
                TaskEngine.getInstance().submit(new TestThread(param));
            }
        }
    }

    3.运行输出

    4.修改测试类

    package com.zyt.creenshot.service.impl;
    
    import com.zyt.creenshot.mapper.TestMapper;
    import com.zyt.creenshot.service.ITestService;
    import com.zyt.creenshot.service.thread.TestThread;
    import com.zyt.creenshot.util.TaskEngine;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @ClassName TestServiceImpl
     * @Description screenShot 梦想家
     * @Author Zhai XiaoTao https://www.cnblogs.com/zhaiyt
     * @Date 2019/1/15 17:34
     * @Version 1.0
     */
    @Service
    public class TestServiceImpl implements ITestService {
    
        @Autowired(required = false)
        private TestMapper testMapper;
    
        @Override
        public List test(){
            List<String> list = new ArrayList<>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            list.add("6");
            list.add("7");
            list.add("8");
            list.add("9");
            List map = testMapper.selectByCount(list);
            System.out.println(map);
            return map;
        }
    
        public static void main(String[] args) {
            testTask();
        }
    
        public static void testTask(){
            List<String> list = new ArrayList<>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            list.add("6");
            list.add("7");
            list.add("8");
            list.add("9");
            Map<String,Object> param = new HashMap<>();
    
    
            for (int i = 0 ;i <3 ; i++) {
                List<String> sublist = list.subList(i * 3, 3*(i+1));
                Map map = (HashMap<String, Object>)((HashMap<String, Object>)param).clone();
                map.put("subList",sublist);
                TaskEngine.getInstance().submit(new TestThread(map));
            }
        }
    }

    5.运行测试

    Over 问题解决

  • 相关阅读:
    漫谈 C++ 的 内存堆 实现原理
    我发起了一个 .Net 开源 数据库 项目 SqlNet
    谈谈 数据库原理
    论 数据库 B Tree 索引 在 固态硬盘 上 的 离散存储
    论 东坡肉 和 红烧肉 的 区别
    浅谈 操作系统原理
    引子 初识
    P2P Downloader
    利用 MessageRPC 和 ShareMemory 来实现 分布式并行计算
    MessageRPC
  • 原文地址:https://www.cnblogs.com/zhaiyt/p/10300449.html
Copyright © 2020-2023  润新知