循环中多线程参数为空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。
原文地址:https://www.cnblogs.com/clnsx/p/12894765.html