Java给多线程编程提供了内置的支持。,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务,能满足程序员编写高效率的程序来达到充分利用 CPU 的目的,实现多线程的方法有四种,继承Thread类,实现Runable接口,Callable接口,或者从线程池中获取。
先了解线程的生命周期
- 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
- 就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
- 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
- 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
- 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
- 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
- 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程池的优点
Java通过Executors提供五种线程池
- newFixedThreadPool(...) 创建固定大小的线程池
- newSingleThreadExecutor(...) 单线程的线程池
- newCachedThreadPool(...) 可缓存的线程池
- newScheduledThreadPool(...) 定时任务调度的线程池
- newSingleThreadScheduledExecutor() 单线程的定时任务调度线程池
优点
- 重用存在的线程,减少对象创建、消亡的开销,性能佳。
- 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
- 提供定时执行、定期执行、单线程、并发数控制等功能。
springboot中配置线程池
代码语言:javascript复制@Configuration
public class GlobalConfig {
/**
* 默认线程池线程池
*
* @return Executor
*/
@Bean
public ThreadPoolTaskExecutor defaultThreadPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数目
executor.setCorePoolSize(16);
//指定最大线程数
executor.setMaxPoolSize(64);
//队列中最大的数目
executor.setQueueCapacity(16);
//线程名称前缀
executor.setThreadNamePrefix("defaultThreadPool_");
//rejection-policy:当pool已经达到max size的时候,如何处理新任务
//CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
//对拒绝task的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//线程空闲后的最大存活时间
executor.setKeepAliveSeconds(60);
//加载
executor.initialize();
return executor;
}
模拟批量发送短信
代码语言:javascript复制//通过注解引入配置
@Resource(name = "defaultThreadPool")
private ThreadPoolTaskExecutor executor;
@ApiOperation(value = "批量给用户发短信")
@PostMapping("/sendMessage")
@Unchecked
public ResultWrapper sendMessage(@ApiParam(required = true, value = "电话集合") @RequestParam List<String> phones) {
try {
if (CollUtil.isNotEmpty(phones)) {
List<List<String>> split = CollUtil.split(phones, 3);
for (List<String> list : split) {
List<Future> futures = new ArrayList<>();
for (String phone : list) {
Future<?> future = executor.submit(() -> {
//发送短信
System.out.println("给" phone "发送短信");
});
futures.add(future);
}
//阻塞等待批量执行结果然后再执行下一个批次
for (Future future : futures) {
while (true) {//CPU高速轮询:每个future都并发轮循,判断完成状态然后获取结果,这一行,是本实现方案的精髓所在。即有10个future在高速轮询,完成一个future的获取结果,就关闭一个轮询
if (future.isDone()) {//获取future成功完成状态
break;
} else {
ThreadUtil.sleep(100);//每次轮询休息1毫秒(CPU纳秒级),避免CPU高速轮循耗空CPU
}
}
}
System.out.println("成功发送3个");
}
}
} catch (Exception e) {
e.printStackTrace();
return ResultWrapper.error().putData("发送失败");
}
return ResultWrapper.ok().putData("发送成功");
}
结果如下