容易出现的问题:
- 1.LinkedBlockingDeque内方法take会导致线程阻塞
- 2.使用poll方法,size为0时会抛异常
@Slf4j
public class ThreadsTest {
private static final ExecutorService executorService = Executors.newFixedThreadPool(11);
private static final int total = 500;
private static final AtomicInteger pageNum = new AtomicInteger(0);
private static final int size = 50;
private static final AtomicInteger remain = new AtomicInteger(50);
public static void main(String[] args) throws InterruptedException {
//模拟待处理的数据
LinkedBlockingDeque<Map<String, Object>> cacheMaps = new LinkedBlockingDeque<>(50);
while (pageNum.incrementAndGet() * size <= total) {
remain.set(50);
//信号量工具类
CountDownLatch countDownLatch = new CountDownLatch(10);
//生产端,单个线程
executorService.execute(() -> produce(cacheMaps));
//多消费
for (int i = 0; i < 10; i++) {
executorService.execute(() -> consumer(cacheMaps, countDownLatch));
}
countDownLatch.await();
}
}
private static void consumer(LinkedBlockingDeque<Map<String, Object>> cacheMaps, CountDownLatch countDownLatch) {
while (remain.decrementAndGet() >= 0) {
try {
System.out.println("消费线程:" + cacheMaps.take()+"\t"+Thread.currentThread().getName());
} catch (InterruptedException e) {
log.info("消费线程被打断!!!");
}
}
countDownLatch.countDown();
System.out.println("消费线程:" + new AbstractMap.SimpleEntry<>("countDownLatch", countDownLatch.getCount())+ "\t"+Thread.currentThread().getName());
}
private static void produce(LinkedBlockingDeque<Map<String, Object>> cacheMaps) {
for (int i = 0; i < size; i++) {
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("key:" + ((pageNum.get()-1) * size+ i) , i);
try {
cacheMaps.put(stringObjectHashMap);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
/**
* @author yilv
* @version 1.0
* @description: 并发同步信息
* @date 2023/1/8 17:52
*/
public class DataSyncTask<T> {
/**
* 现场注册池
*/
private final Map<Long, FutureTask<Void>> futureTasks = new HashMap<>();
/**
* 线程池
*/
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
}
public void sync(List<T> data){
data.forEach(f -> {
FutureTask<Void> voidFutureTask = new FutureTask<>(() -> {
// todo 同步数据库目录信息
return null;
});
//放入注册池、用于回调
futureTasks.put(IdWorker.getId(), voidFutureTask);
executorService.submit(voidFutureTask);
});
futureTasks.forEach((id, futureTask) -> {
try {
//未启动或者已启动的状态时,调用FutureTask对象的get方法会将导致调用线程阻塞。当FutureTask处于已完成的状态时,调用FutureTask的get方法会立即放回调用结果或者抛出异常
futureTask.get();
} catch (InterruptedException | ExecutionException e) {
//todo 处理异常信息
}
});
}
}
解决方案:创建spring工具类,手动获取。或者使用hutool里面的工具类
/**
* @author yilv
* @version 1.0
* @description: TODO 通用bean获取类
* @date 2021/7/21 16:17
*/
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
/**
* 上下文对象实例
*/
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 获取applicationContext
*
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取 Bean.
*
* @param name
* @return
*/
public static <T> T getBean(String name) {
return (T)getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
*
* @param clazz
* @param
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
*
* @param name
* @param clazz
* @param
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
springboot单实例多线程下获取同一个bean,获取的对象引用相同,业务逻辑互相产生覆盖。最常见的场景是多次启动同一个定时任务
/**
* @author yilv
* @version 1.0
* @description: 启用定时任务&配置线程池
* @date 2023/1/13 18:43
*/
@Configuration
@EnableScheduling
public class TaskConfig {
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(20);
threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
return threadPoolTaskScheduler;
}
}
/**
* @author yilv
* @version 1.0
* @description: 测试单例通用成员变量
* @date 2023/1/13 18:03
*/
@Component
public class DemoTask implements Runnable {
public Integer count=20;
@Override
public void run() {
//todo
while (count>0){
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(DateUtil.now()+":"+Thread.currentThread().getName()+"\t"+count--);
}
}
}
@Slf4j
@EnableScheduling
@SpringBootTest
class DemoTaskTest {
@Resource
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Test
public void test01(){
HashMap<String, ScheduledFuture<?>> scheduledFutureMap = new HashMap<>();
String everyDay = "* * * * * ?";
for (int i = 0; i < 10; i++) {
String idStr = IdWorker.getIdStr();
ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(SpringUtil.getBean(DemoTask.class), m -> new CronTrigger(everyDay).nextExecutionTime(m));
scheduledFutureMap.put(idStr,schedule);
}
//任务是否结束
scheduledFutureMap.forEach((k,v)->{
try {
Object o = v.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
log.debug("任务结束:{}",k);
});
}
}
2023-01-13 18:49:58:taskExecutor-9 20
2023-01-13 18:49:58:taskExecutor-10 16
2023-01-13 18:49:58:taskExecutor-3 15
2023-01-13 18:49:58:taskExecutor-1 14
2023-01-13 18:49:58:taskExecutor-8 17
2023-01-13 18:49:58:taskExecutor-7 13
2023-01-13 18:49:58:taskExecutor-5 12
2023-01-13 18:49:58:taskExecutor-6 18
2023-01-13 18:49:58:taskExecutor-4 19
2023-01-13 18:49:58:taskExecutor-2 11
2023-01-13 18:50:00:taskExecutor-9 10
2023-01-13 18:50:00:taskExecutor-1 8
2023-01-13 18:50:00:taskExecutor-3 9
2023-01-13 18:50:00:taskExecutor-10 10
2023-01-13 18:50:00:taskExecutor-8 7
2023-01-13 18:50:00:taskExecutor-7 6
2023-01-13 18:50:00:taskExecutor-5 5
2023-01-13 18:50:00:taskExecutor-6 4
2023-01-13 18:50:00:taskExecutor-4 3
2023-01-13 18:50:00:taskExecutor-2 2
2023-01-13 18:50:02:taskExecutor-6 1
2023-01-13 18:50:02:taskExecutor-2 1
2023-01-13 18:50:02:taskExecutor-4 1
2023-01-13 18:50:02:taskExecutor-3 -5
2023-01-13 18:50:02:taskExecutor-10 -4
2023-01-13 18:50:02:taskExecutor-8 -4
2023-01-13 18:50:02:taskExecutor-7 -3
2023-01-13 18:50:02:taskExecutor-9 -2
2023-01-13 18:50:02:taskExecutor-5 -1
2023-01-13 18:50:02:taskExecutor-1 0
2023-01-13 18:50:02.133 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850825032507395
2023-01-13 18:50:03.007 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850825032507394
2023-01-13 18:50:04.008 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850825032507393
2023-01-13 18:50:05.008 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850825032507399
2023-01-13 18:50:06.007 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850825032507398
2023-01-13 18:50:07.007 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850825032507397
2023-01-13 18:50:08.007 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850824965398530
2023-01-13 18:50:09.007 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850825032507396
2023-01-13 18:50:10.007 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850825032507401
2023-01-13 18:50:11.007 DEBUG 21728 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613850825032507400
指定bean的作用域为@Scope(“prototype”),采用多例模式拒绝使用一个bean
处理结果:
2023-01-13 18:57:54:taskExecutor-2 20
2023-01-13 18:57:54:taskExecutor-9 20
2023-01-13 18:57:54:taskExecutor-7 20
2023-01-13 18:57:54:taskExecutor-10 20
2023-01-13 18:57:54:taskExecutor-8 20
2023-01-13 18:57:54:taskExecutor-6 20
2023-01-13 18:57:54:taskExecutor-5 20
2023-01-13 18:57:54:taskExecutor-3 20
2023-01-13 18:57:54:taskExecutor-1 20
2023-01-13 18:57:54:taskExecutor-4 20
2023-01-13 18:57:56:taskExecutor-1 19
2023-01-13 18:57:56:taskExecutor-4 19
2023-01-13 18:57:56:taskExecutor-3 19
2023-01-13 18:57:56:taskExecutor-9 19
2023-01-13 18:57:56:taskExecutor-2 19
......................................
2023-01-13 18:58:32:taskExecutor-8 1
2023-01-13 18:58:32:taskExecutor-6 1
2023-01-13 18:58:32:taskExecutor-7 1
2023-01-13 18:58:32:taskExecutor-5 1
2023-01-13 18:58:32:taskExecutor-10 1
2023-01-13 18:58:32:taskExecutor-1 1
2023-01-13 18:58:32:taskExecutor-9 1
2023-01-13 18:58:32.257 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352641
2023-01-13 18:58:33.008 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352642
2023-01-13 18:58:34.007 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352650
2023-01-13 18:58:35.007 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352645
2023-01-13 18:58:36.008 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352646
2023-01-13 18:58:37.006 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352643
2023-01-13 18:58:38.007 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352644
2023-01-13 18:58:39.007 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352649
2023-01-13 18:58:40.008 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352647
2023-01-13 18:58:41.008 DEBUG 28412 --- [ main] com.yilv.web.task.DemoTaskTest : 任务结束:1613852823530352648
异常描述:
java hotspot 64-bit server vm warning:info:os::commit_memory(0xxxxxxxxxxxx,19xxxxx,0) failed;error=‘not enough space’
failed to start thread - pthread_create failed for attributes: stacksize:2048k,guardize:ok,detached
解决方法:
Lambda$534/0x0000000800546840@cbd40c1 rejected from java.util.concurrent.ThreadPoolExecutor@4fa86cb8[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 11]
设置的线程池队列满了,可以扩大线程池增加溢出策略来解决