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执行时没锁了。罪过罪过