zoukankan      html  css  js  c++  java
  • 循环中多线程参数为空bug

    循环中多线程参数为空bug

    问题来由

    在循环中使用多线程执行是常见的做法,使用map作为多线程内部的函数传入参数,然而在多线程后使用clear清空map中的内容,就会发现多线程中的数据没了。如下所示:

    package com.example.redis_test.thread;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.concurrent.ThreadPoolExecutor;
    
    
    public class ThreadConfig {
        //使用@Bean返回一个spring接管的bean对象。
      	@Bean
        ThreadPoolTaskExecutor defaultThreadPoolExecutor() {
            ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
            threadPoolTaskExecutor.setCorePoolSize(0);
            threadPoolTaskExecutor.setCorePoolSize(10);
            threadPoolTaskExecutor.setQueueCapacity(20);
            threadPoolTaskExecutor.setRejectedExecutionHandler(new 				ThreadPoolExecutor.CallerRunsPolicy());
            threadPoolTaskExecutor.initialize();
            return threadPoolTaskExecutor;
        }
    }
    

    测试一:

    package com.example.redis_test;
    
    import com.example.redis_test.thread.ThreadConfig;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @SpringBootTest
    class RedisTestApplicationTests {
    
        @Autowired
        ThreadPoolTaskExecutor threadPoolTaskExecutor;
        @Test
        void contextLoads() {
            Map<String,String> map = new HashMap<>();
            for(int i = 0; i < 5; i++){
                map.put(String.valueOf(i),String.valueOf(i));
                String k = String.valueOf(i);
                threadPoolTaskExecutor.execute(()->{
                    System.out.println(map.get(String.valueOf(k)));
                });
                map.clear();
            }
        }
    
    }
    
    /**
     * 输出结果:null, null, null, null, null
     * 
     */
    

    测试二:

    package com.example.redis_test;
    
    import com.example.redis_test.thread.ThreadConfig;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @SpringBootTest
    class RedisTestApplicationTests {
    
        @Autowired
        ThreadPoolTaskExecutor threadPoolTaskExecutor;
        @Test
        void contextLoads() {
    
            for(int i = 0; i < 5; i++){
                Map<String,String> map = new HashMap<>();
                map.put(String.valueOf(i),String.valueOf(i));
                String k = String.valueOf(i);
                threadPoolTaskExecutor.execute(()->{
                    System.out.println(map.get(String.valueOf(k)));
                });
            }
        }
    
    }
    /**
     * 输出结果:0, 1, 2, 3, 4
     *
     */
    

    分析

    首先这是因为内存模型的不熟练。

    下图是上述场景的内存图

    主线程和线程1都会引用map对象。所以我们看代码

    threadPoolTaskExecutor.execute(()->{
                    System.out.println(map.get(String.valueOf(k)));
                });
                map.clear();
    

    这一段里如果main运行的速度比线程的速度快,那么就会将map对象清空。

    解决办法

    1. 在for循环里使用Map<String,String> map = new HashMap<>();(按照测试2)
    2. Map<String,String> keyMap = new HashMap<>(map); 然后在多线程使用keyMap,主线程使用map。
  • 相关阅读:
    原型,构造函数,实例,__proto__
    To me
    那么再会吧!OI!(HNOI2019退役记)
    中山纪念中学培训杂题(难的都不在这里面qwq)
    关于菜鸡我
    树链剖分讲解
    [luogu] P4823 [TJOI2013]拯救小矮人(贪心)
    [luogu] P4551 最长异或路径(贪心)
    [luogu] P4364 [九省联考2018]IIIDX(贪心)
    [luogu] P4155 [SCOI2015]国旗计划(贪心)
  • 原文地址:https://www.cnblogs.com/clnsx/p/12894765.html
Copyright © 2011-2022 走看看