在网上找了很多很多资料,使用多线程、异步线程等等,很多配置,方法也多多;
那么,我向来都是以简单,够用为目标,下面我介绍的就是我认为已经非常非常简单的异步线程使用的方法了。
说到简单,当然是使用注解。
进入正题:
先上个目录结构:
好了,我们这次是在springboot里面使用的,不用导啥包。
我们先创个异步线程的配置类, 我的叫ThreadConfig,你们随意:
package com.async.config; import java.util.concurrent.Executor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @ComponentScan("com.async.service") @EnableAsync public class ThreadConfig { /** * 执行需要依赖线程池,这里就来配置一个线程池 * @return */ // 当池子大小小于corePoolSize,就新建线程,并处理请求 // 当池子大小等于corePoolSize,把请求放入workQueue(QueueCapacity)中,池子里的空闲线程就去workQueue中取任务并处理 // 当workQueue放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理 // 当池子的线程数大于corePoolSize时,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁 @Bean("getExecutor") public Executor getExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //设置核心线程数 executor.setCorePoolSize(10); //设置最大线程数 executor.setMaxPoolSize(100); //线程池所使用的缓冲队列 executor.setQueueCapacity(250); //设置线程名 executor.setThreadNamePrefix("JcTest-Async"); //设置多余线程等待的时间,单位:秒 //executor.setKeepAliveSeconds(); // 初始化线程 executor.initialize(); return executor; } }
向来我都不做代码过多的解释,因为我的代码注释 已!经!足!够!详!细 ! 了!
不过还是得提一下这俩注解,
@ComponentScan("com.async.service") 这个是告诉全世界,我即将开启异步线程的业务方法是哪个
@EnableAsync 这个! 必须有! 告诉全世界允许我使用异步线程
好了,接下来我们创建一下业务层的东西吧:
AsyncTestService:
package com.async.service; public interface AsyncTestService { /** * 这里将会在impl里标注为异步任务,在执行此方法的时候,会单独开启线程来执行 */ void function1() throws InterruptedException; void function2(); }
AsyncTestServiceImpl:
package com.async.service.impl; import com.async.service.AsyncTestService; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.UUID; @Service public class AsyncTestServiceImpl implements AsyncTestService { /** * 这里进行标注为异步任务,在执行此方法的时候,会单独开启线程来执行 */ @Async("getExecutor") public void function1() throws InterruptedException { System.out.println("f1 : " + Thread.currentThread().getName() + " " + UUID.randomUUID().toString()); // try { // Thread.sleep(10000); // System.out.println("EEEE"); // } catch (InterruptedException e) { // e.printStackTrace(); // } //故意等10秒,那么异步线程开起来,这样明显看到 2方法不用等1方法执行完再调用了 Thread.sleep(10000); System.out.println("EEEE"); } @Async("getExecutor") public void function2() { System.out.println("f2 : " + Thread.currentThread().getName() + " " + UUID.randomUUID().toString()); try { Thread.sleep(100); System.out.println("aaaa"); } catch (InterruptedException e) { e.printStackTrace(); } } }
代码不走读了,但是必须得提下注解:
@Async这个是告诉全世界,这里! 只要被调用,就是会开启一个异步线程。
至于后面加上("getExecutor"),是为了指定读取自己写的配置信息例如线程名称那些。
最后是TestController:
package com.async.test; import com.async.service.AsyncTestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @Autowired AsyncTestService asyncTestService; @GetMapping("/test") public void test() throws InterruptedException { // for (int i = 0; i < 10; i++) { // asyncTestService.function1(); // 执行异步任务 // asyncTestService.function2(); // } asyncTestService.function1(); // 执行异步任务 asyncTestService.function2(); } }
启动类没啥改变:
package com.async; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AsyncApplication { public static void main(String[] args) { SpringApplication.run(AsyncApplication.class, args); } }
好了,跑一下:
结合
代码看看,懂了吧应该。
稍微解释一哈:
asyncTestService.function1(); // 执行异步任务
asyncTestService.function2();
本来按照正常,肯定是按顺序执行的,也就是先编译完方法1,输出所有东东,再到方法2;
然后我们配了异步,而且在方法1那边sleep了蛮久的, 所以你看到输出结果,显然方法2自己单飞了,并没有等方法1;
这结果是我们想要的。
最后提醒一下, 有没有发现,我这个例子分了好多层,而且连impl都分了的。其实不分那么细也没问题,但是!!!
有个需要注意的! 利用@Async注解的方法, 不能跟调用它的方法放在同个类里面!!!! 否则会循环依赖错误!!!
还有一个需要注意的,@Async所修饰的函数不要定义为static类型,这样异步调用不会生效!
好了,就到这吧。