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。
  • 相关阅读:
    Hibernate 5.x 生成 SessionFactory 源码跟踪分析
    编译iftop
    Too many open files
    ffmpeg指定网卡
    abrt-hook-ccpp使用CPU太多
    ffplay播放时显示信息的意义
    Windows换行符和Linux换行符的替换
    directshow在WIN10下的一个BUG
    使用mirrordriver截屏
    mac xmind 激活
  • 原文地址:https://www.cnblogs.com/clnsx/p/12894765.html
Copyright © 2011-2022 走看看