使用线程池发送短信

DifficultStill. 2023-02-22 02:13:29 17538 0 0 0

线程池发送短信

1.什么是线程池

线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然在创建线程后自动启动这些任务。

2.为什么要使用线程池呢?

因为频繁的开启线程或者停止线程,线程需要重新从cpu从就绪状态调度到运行状态,需要发送cpu的上下文切换,效率非常低。
线程池是复用机制,提前创建好一些固定的线程数一直在运行状态,实现复用,从而可以减少就绪到运行状态的切换。

3.线程池的使用流程:

图片alt
1.首先在银行开业的时候就只会开启2个窗口,并且什么时候也不会关闭-核心线程数
2.当人数大于2并且小于5就让多出来的去等候厅等待-阻塞队列
3.ok,现在来的人数大于5并且小于8了,此时正在休息的3个窗口就打开了,于是乎就一共有了5个窗口 -最大线程数
4.当人数实在是太多了,这次一下来了10个人,咱们窗口以及等候厅都无法放下就会执行拒绝策略!
5.好了现在人数少了,另外3个空闲窗口也没有生意了,为了节约成本呢就关闭了 -超时等待

4.代码实现

1.首先,咱们使用异步调用线程池的方式来实现 线程池发送短信;
@enableasync:EnableAsync注解的意思是可以异步执行,就是开启多线程的意思。 可以标注在方法、类上。
@Async:Async的注解放在方发是异步调用方法;放在类上是表示这个类中所有方法都是异步方法。
2.
(1)咱们创建一个ThreadPoolTaskExecutor的子类来打印信息

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author 苏海龙
 * @version 1.0
 * @description: TODO
 * @date 2023/2/22 8:45
 */

@Slf4j
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {



    private void showThreadPoolInfo(String prefix) {

        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();



        if (null == threadPoolExecutor) {

            return;

        }



        log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",

                this.getThreadNamePrefix(),

                prefix,

                threadPoolExecutor.getTaskCount(),

                threadPoolExecutor.getCompletedTaskCount(),

                threadPoolExecutor.getActiveCount(),

                threadPoolExecutor.getQueue().size());

    }



    @Override

    public void execute(Runnable task) {

        showThreadPoolInfo("1. do execute");

        super.execute(task);

    }



    @Override

    public void execute(Runnable task, long startTimeout) {

        showThreadPoolInfo("2. do execute");

        super.execute(task, startTimeout);

    }



    @Override

    public Future<?> submit(Runnable task) {

        showThreadPoolInfo("1. do submit");

        return super.submit(task);

    }



    @Override

    public <T> Future<T> submit(Callable<T> task) {

        showThreadPoolInfo("2. do submit");

        return super.submit(task);

    }



    @Override

    public ListenableFuture<?> submitListenable(Runnable task) {

        showThreadPoolInfo("1. do submitListenable");

        return super.submitListenable(task);

    }



    @Override

    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {

        showThreadPoolInfo("2. do submitListenable");

        return super.submitListenable(task);

    }
}

(2)咱们创建一个线程池配置类来异步调用

import lombok.extern.slf4j.Slf4j;
import org.mybatis.logging.Logger;
import org.mybatis.logging.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.sql.Time;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author 苏海龙
 * @version 1.0
 * @description: TODO
 * @date 2023/2/22 8:14
 */

@Configuration
@Slf4j
//告诉他这是 线程池异步调用
@EnableAsync
public class ExecutorConfig {
    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {

        log.info("start asyncServiceExecutor");
//new出一个咱们上面创建好的ThreadPoolTaskExecutor的子类对象
        VisiableThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();

        //配置核心线程数

        executor.setCorePoolSize(10);

        //配置最大线程数

        executor.setMaxPoolSize(20);

        //配置队列大小

        executor.setQueueCapacity(100);
        //设置超时等待
//        executor.setKeepAliveSeconds(100);

        //配置线程池中的线程的名称前缀

        executor.setThreadNamePrefix("async-service-");



        // rejection-policy:当pool已经达到max size的时候,如何处理新任务

        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        //执行初始化

        executor.initialize();

        return executor;

    }


}

(3)咱们写个实现方法来发送短信并且使用@Async来异步调用咱们创建好的 线程池配置类。

    @Override
    @Async("asyncServiceExecutor")
    public String shendmoblie(PhoneVo phoneVo) {
        log.info("start executeAsync");
        System.out.println("异步线程要做的事情");
        System.out.println("可以在这里执行批量插入等耗时的事情");
       //生成随机4位数
		String s = RandomUtil.randomNumbers(4);
        //发送短信
		DuanxinUtils.sendDuanxin(phoneVo.getPhone(),s);
        log.info("end executeAsync");
        return "发送成功了";
    }

注意@Async注解里面的值要和咱们bean注入的值一样
图片alt
图片alt
(4)使用jmeter来进行测试:
图片alt
(5)查看结果:
图片alt
总结:当前已经提交了47个任务,完成了40个,当前有7个线程在处理任务,还剩0个任务在队列中等待,线程池的基本情况一路了然;