循环中多线程参数为空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对象清空。
解决办法
- 在for循环里使用
Map<String,String> map = new HashMap<>();
(按照测试2) Map<String,String> keyMap = new HashMap<>(map);
然后在多线程使用keyMap,主线程使用map。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步