SpringBoot使用多线程简单方法:地址
线程安全查阅资料参考:地址
背景: 经过上述资料查看,我想写个方法(依靠notify()唤醒,依靠wait()等待)实现两个线程轮流打印。
实现:
1.线程池配置
@Configuration
@EnableAsync
public class AsyncConfiguration {
@Bean("ceshiAsync")
public Executor doSomethingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数:线程池创建时候初始化的线程数
executor.setCorePoolSize(3);
// 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(4);
// 缓冲队列:用来缓冲执行任务的队列
executor.setQueueCapacity(10);
// 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(6);
// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("测试线程-");
// 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.initialize();
return executor;
}
}
2.轮流打印的方法( 两个方法完全一样,主要依靠list.notify(); list.wait();)
@Async("ceshiAsync")
public void aaa(List<Integer> list) {
System.out.println("线程A等待获取i锁");
synchronized (list) {
try {
System.out.println("线程A获取了i锁");
while (list.get(0)>-1){
log.info("线程A:"+list.get(0));
list.set(0,list.get(0)-1);
list.notify();
list.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Async("ceshiAsync")
public void bbb(List<Integer> list) {
System.out.println("线程B等待获取i锁");
synchronized (list) {
try {
System.out.println("线程B获取了i锁");
while (list.get(0)>-1){
log.info("线程B:"+list.get(0));
list.set(0,list.get(0)-1);
list.notify();
list.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.调用方法
List<Integer> list=new ArrayList<>();
@Test
@GetMapping(value = "ccc")
public void ccc(){
list.add(100);
ceshiService.aaa(list);
System.out.println("-----------");
ceshiService.bbb(list);
}
实现效果
踩坑
1.@Async注解未生效
原因:测试图方便把注解的方法直接写在方法,调用时直接用方法名调用,固注解未生效,这种写法会导致注解失效,例如事务的注解,但是与事务不同的时注入自身时记得添加 @Lazy注解,避免循环依赖。
2.current thread is not owner(当前线程不是所有者)错误
不要直接用Integer对象当锁,查询资料Integer内部的int值是不可改变的,估计是值改变时地址变了导致报这个错。
3.执行到一半报错
效果:
原因:有些测试不适合用@Test注解,例如这里线程池出问题,事后想想这报错完全是理所应当的事情,亏我还在怀疑是notify方法提前把锁释放了导致wait执行时没锁了。罪过罪过