一文看懂线程的生命周期,利用线程池模拟群发短信

2020-11-25 15:23:27 浏览数 (1)

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("发送成功");
    }

结果如下

0 人点赞