提示:本文没有使用原生的创建线程方式,默认已掌握创建线程的四种方式
全文基于SpringBoot框架,要求读者掌握SpringBoot操作
本人能力有限,如有遗漏或错误,敬请指正,谢谢
1.Java多线程基本概念和常用API(面试高频)
2.线程安全问题(synchronized解决,各种类型全)
3.Java并发线程池使用和原理(通俗易懂版)
4.基于SpringBoot+Async注解整合多线程
学习一门技术最好使用wwh方法
what:这门技术是什么
why:为什么用这个技术,使用会有什么优化
how:怎么使用
提示:以下是本篇文章正文内容,下面案例可供参考
先谈谈单线程的使用:
在普通业务下,要查询多个表,调用多个接口(比如根据id查询A表,B表,C表),同步进行,要等待每个接口执行后才走下一个接口,耗时久
为了提高效率,使用多线程异步的方式:
代码如下(示例):手动开启多线程,如果业务都需要,那么每个都要自己编码
public static void main(String[] args) {
System.out.println("主线程开启");
//创建线程池
ThreadPoolExecutor executor=new ThreadPoolExecutor(5,5,10, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
executor.execute(()->{
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"多线程执行");
});
System.out.println("主线程结束");
}
@Async生效条件:
1.方法是public的
2.在同一个类中调用无效,因为它绕过代理并直接调用底层方法。如A和B方法在一个类中,在B方法上加@Async注解,A调用B是不会使用多线程的
代码如下(示例):
在多线程方法中睡眠3s更方便体现异步执行
@RequestMapping("/test")
public void test(){
System.out.println("主线程"+Thread.currentThread().getName()+"开启");
threadTask.test();
System.out.println("主线程结束");
}
@Async()
void test() {
System.out.println(Thread.currentThread().getName()+"被调用了");
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束了");
}
输出:
主线程 xxx 开启
主线程结束
异步线程名 被调用
异步线程名 结束了
默认使用Spring创建ThreadPoolTaskExecutor。
默认核心线程数:8,最大线程数:Integet.MAX_VALUE
队列使用LinkedBlockingQueue,容量是:Integet.MAX_VALUE
空闲线程保留时间:60s
线程池拒绝策略:AbortPolicy。
ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装处理。
Spring项目用ThreadPoolTaskExecutor更方便
@Configuration
@EnableAsync //启用异步任务,开启多线程 配合@Async注解在方法上,表示这个方法被调用时,会从线程池拿出线程来调用
public class ThreadConfig {
@Bean
public ThreadPoolTaskExecutor executor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(15);
//配置最大线程数
executor.setMaxPoolSize(30);
//配置队列大小
executor.setQueueCapacity(1000);
//线程的名称前缀
executor.setThreadNamePrefix("Executor-");
//线程活跃时间(秒)
//executor.setKeepAliveSeconds(60);
//等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
//设置拒绝策略
//executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
插入100w数据,分10次插入,采用多线程方式
持久层使用mybatis-plus
使用JUC线程类
@RestController
public class TestController {
@Autowired
private ThreadPoolTaskExecutor executor;
@Autowired
private UserService userService;
@RequestMapping("/test1")
public void test1(){
//10表示线程有10个
CountDownLatch cdl = new CountDownLatch(10);
Long start=System.currentTimeMillis();
for(int i=1;i<=10;i++){
executor.execute(() -> {
System.out.println("开启线程:"+Thread.currentThread().getName());
List<User> list=new ArrayList<>();
for(int j=1;j<=100000;j++){
list.add(new User(UUID.randomUUID().toString(),"aaa"));
}
userService.saveBatch(list,10000);
//每执行完一个线程,就减一
cdl.countDown();
});
}
try {
//如果线程数不为0,就一直等待,不用这个判断算不出 本次插入时间
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Long end=System.currentTimeMillis();
System.out.println(end-start);
}
}
使用步骤:
1.不想用Spring配置的线程池,则自己创建Bean
2.开启@EnableAsync异步
3.在调用的方法加上@Async注解