springboot 异步以及自定义线程池

一、springboot 异步默认线程池

==① 启动类开启异步==
/**
 * @PackageName:com.dmo
 * @ClassName:App
 * @Description: @EnableAsync  开启springboot 异步(线程池)
 * @Author:
 */
@SpringBootApplication
@EnableAsync
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}


==Controller 返回值类型为 String,不能用String 接收,而是Future<String>==
@GetMapping("/springpool")
    public String SpringPool() throws InterruptedException, ExecutionException {
       Future<String> date1 =  demoService.TestData1();
       Future<String> date2 =  demoService.TestData2();
       String result = date1.get() + "  |  " +date2.get() ;
       System.out.println(result);
       return  result;
    }


==③实现类Service==
/**
 * @PackageName:com.dmo.service
 * @ClassName:DemoService
 * @Description: 测试springboot 两个异步线程
 * @Author:
 */
@Service
public class DemoService {

    @Async
    public Future<String>  TestData1() throws InterruptedException {
        //休息3s
        TimeUnit.SECONDS.sleep(3);
        String result = "线程名:"+Thread.currentThread().getName()+" 当前时间戳:"+System.currentTimeMillis() ;
        System.out.println(result);
        return  new AsyncResult<String>(result);
    }

    @Async
    public Future<String>  TestData2() throws InterruptedException {
        TimeUnit.SECONDS.sleep(3);
        String result = "线程名:"+Thread.currentThread().getName()+" 当前时间戳:"+System.currentTimeMillis() ;
        System.out.println(result);
        return  new AsyncResult<String>(result);
    }
}


执行结果在这里插入图片描述

二、自定义线程池

==①配置线程池==
Executor 接口是 Java 原生的线程池实现,提供了基本的线程池功能。
/**
 * @PackageName:com.dmo.config
 * @ClassName:ThreadPoolExecutorConfig
 * @Description: 配置线程池
 * @Author: john
 */
@Configuration
public class ThreadPoolExecutorConfig {
    //核心线程数 = 当前cpu线程数量 +1
    //setDaemon 守护线程
    private static final int THREADS = Runtime.getRuntime().availableProcessors() + 1;
    final ThreadFactory threadFactory = new ThreadFactoryBuilder()
            .setNameFormat("My_Thread -> %d")
            .setDaemon(true)
            .build();

    //线程池名
    @Bean("myTaskExecutor")
    public Executor myTaskExecutor() {
        //7 个参数 =
        // 核心线程数,
        // 最大线程数,
        // 线程存活时间,
        // 单位,
        // 线程存放阻塞队列,
        // 线程创建工厂
        // 超出线程的拒绝策略 (4种,默认超出报 Exception )
        return new ThreadPoolExecutor(THREADS,
                                            9,
                                            5,
                             TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10),
                                threadFactory,
                         (runnable, executor) -> {
                        // 打印日志,添加监控等
                        System.out.println("超出最大线程数,已被拒绝!"); });
    }
}ThreadPoolTaskExecutorSpring 框架提供的一个类,它提供了更多的扩展功能,比如线程池的前缀、线程池的拒绝策略、线程池的监控等

@Configuration
public class ThreadPoolTaskExecutorConfig {
    private static final int corePoolSize = 4;       		// 核心线程数
    private static final int maxPoolSize = 4;			    // 最大线程数
    private static final int keepAliveTime = 10;			// 允许线程空闲时间(单位:默认为秒)
    private static final int queueCapacity = 15;			// 缓冲队列数
    private static final String threadNamePrefix = "Thread-PushMessage-Service-"; // 线程池名前缀

    @Bean("pushMessageExecutor")
    public ThreadPoolTaskExecutor pushMessageExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveTime);
        executor.setThreadNamePrefix(threadNamePrefix);
        // setWaitForTasksToCompleteOnShutdown(true): 该方法用来设置 线程池关闭 的时候 等待 所有任务都完成后,再继续 销毁 其他的 Bean,这样这些 异步任务 的 销毁 就会先于 数据库连接池对象 的销毁。
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // setAwaitTerminationSeconds(60): 该方法用来设置线程池中 任务的等待时间,如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
        executor.setAwaitTerminationSeconds(60);
        // 线程池对拒绝任务的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
}
② 异步指定线程池
@Async("myTaskExecutor")
    七大参数:
    1 corePoolSize:线程池核心线程数量,核心线程不会被回收,即使没有任务执行,也会保持空闲状态。如果线程池中的线程少于此数目,则在执行任务时创建。
    2 maximumPoolSize:池允许最大的线程数,当线程数量达到corePoolSize,且workQueue队列塞满任务了之后,继续创建线程。
    3 keepAliveTime:超过corePoolSize之后的“临时线程”的存活时间。
    4 unit:keepAliveTime的单位。
    5 workQueue:当前线程数超过corePoolSize时,新的任务会处在等待状态,并存在workQueue中,BlockingQueue是一个先进先出的阻塞式队列实现,底层实现会涉及Java并发的AQS机制,有关于AQS的相关知识,我会单独写一篇,敬请期待。
    6 threadFactory:创建线程的工厂类,通常我们会自顶一个threadFactory设置线程的名称,这样我们就可以知道线程是由哪个工厂类创建的,可以快速定位。
    7 handler:线程池执行拒绝策略,当线数量达到maximumPoolSize大小,并且workQueue也已经塞满了任务的情况下,线程池会调用handler拒绝策略来处理请求。

    四大拒绝策略:
    1 AbortPolicy:为线程池默认的拒绝策略,该策略直接抛异常处理。
    2 DiscardPolicy:直接抛弃不处理。
    3 DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务
    4 CallerRunsPolicy:使用当前调用的线程来执行此任务
例。。。。。。。。。。。。。。。。。。。。。。。
多线程调用 service,结果汇总,节约时间
controller

    @Autowired
    private Test test;
    @RequestMapping("/test1/find3")
    public String find23() throws InterruptedException, ExecutionException {
        long start = System.currentTimeMillis();
        CountDownLatch countDownLatch = new CountDownLatch(2);
        //线程1执行service 的 a方法
        Future<Integer> a = test.a(countDownLatch);
        //线程2执行service 的 b方法
        Future<Integer> b = test.b(countDownLatch);
        //阻塞等待线程全部执行完毕
        countDownLatch.await();
        System.out.println(a.get());
        System.out.println(b.get());
        //汇总每个线程的调用结果
        System.out.println("a+b="+(a.get()+b.get()));

        long cost = System.currentTimeMillis() - start;
        String logStr = String.format("查询耗时: %s ms",  cost);
        log.info(logStr);
        return logStr;
    }





service

@Service
public class Test {
   @Async()
    public Future<Integer> a(CountDownLatch countDownLatch) throws InterruptedException {
        System.out.println("a");
        countDownLatch.countDown();
        Thread.sleep(2000);
        System.out.println("a结束时间:"+System.currentTimeMillis());
        return  new AsyncResult<Integer>(1);
    }
    @Async()
    public Future<Integer> b(CountDownLatch countDownLatch) throws InterruptedException {
        System.out.println("b");
        countDownLatch.countDown();
        Thread.sleep(2000);
        System.out.println("b结束时间:"+System.currentTimeMillis());
        return  new AsyncResult<Integer>(2);
    }

}

在service中主动使用线程池!!!

注意,核心线程数此处设置为4 ,程序始终会占用4个核心线程不释放。

@Resource
@Qualifier("pushMessageExecutor")
private ThreadPoolTaskExecutor  threadPoolTaskExecutor;



public void executeAsyncTask() {
      taskExecutor.execute(() -> {
          // 任务代码
      });
  }



如果想临时使用多个线程,之后再释放全部线程,这样写

   @RequestMapping("/hello100")
    public void sayHello100() throws InterruptedException {
        // 10s 后释放线程
        // 默认创建9个核心线程,若任务超过9个,则加入队列由核心线程处理,若队列也满了,则创建新线程处理,队列里的仍然由核心线程处理,线程最多不能超过50个,新创建的线程会等10s,10s没有任务,则销毁新创建的线程,
        // 队列满了才会才能创建新线程,否则一直使用核心线程
        ThreadPoolExecutor executor = new ThreadPoolExecutor(9, 50, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1));
        executor.allowCoreThreadTimeOut(true); // 核心线程若 10s没任务  也会销毁。
        for (int i = 0; i < 30; i++) {
            executor.execute( new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "处理任务ing...");
                }
            });
        }
    }
***************** 建议使用异步线程:@Async  如下实例 **********************
例:
@SpringBootApplication
@EnableAsync
public class SpringbootjpaApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootjpaApplication.class, args);
    }
}

线程池:
@Configuration
public class ThreadPoolTaskExecutorConfig {
    private static final int corePoolSize = 4;       		// 核心线程数
    private static final int maxPoolSize = 4;			    // 最大线程数
    private static final int keepAliveTime = 10;			// 允许线程空闲时间(单位:默认为秒)
    private static final int queueCapacity = 15;			// 缓冲队列数
    private static final String threadNamePrefix = "MyThreadPool-Service-"; // 线程池名前缀

    @Bean("MyThreadPool")
    public ThreadPoolTaskExecutor pushMessageExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveTime);
        executor.setThreadNamePrefix(threadNamePrefix);
        // setWaitForTasksToCompleteOnShutdown(true): 该方法用来设置 线程池关闭 的时候 等待 所有任务都完成后,再继续 销毁 其他的 Bean,这样这些 异步任务 的 销毁 就会先于 数据库连接池对象 的销毁。
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // setAwaitTerminationSeconds(60): 该方法用来设置线程池中 任务的等待时间,如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
        executor.setAwaitTerminationSeconds(60);
        // 线程池对拒绝任务的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
}
controller

    @RequestMapping("pool")
    public void pool()  {
        userService.testpool();
    }
serviceimpl

@Service
public class UserServiceImpl implements UserService {

	//一定要加这个
    @Autowired
    @Lazy 
    private UserServiceImpl userServiceimpl;
    
	@Override
    public void testpool() {
	    System.out.println("主线程:"+Thread.currentThread().getName()+"---start--- "+ new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss").format(new Date()));
        userServiceimpl.pool0();
        userServiceimpl.pool1();
        System.out.println("主线程:"+Thread.currentThread().getName()+"---end--- "+ new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss").format(new Date()));
	}
	
    @Async("MyThreadPool")
    public void pool0() {
        System.out.println("线程池:"+Thread.currentThread().getName()+"---start--- "+ new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss").format(new Date()));
        try { Thread.sleep(5000L);}catch (Exception e){}
        System.out.println("线程池:"+Thread.currentThread().getName()+"---end--- "+ new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss").format(new Date()));
    }

    @Async("MyThreadPool")
    public void pool1() {
        System.out.println("线程池:"+Thread.currentThread().getName()+"---start--- "+ new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss").format(new Date()));
        try { Thread.sleep(5000L);}catch (Exception e){}
        System.out.println("线程池:"+Thread.currentThread().getName()+"---end--- "+ new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss").format(new Date()));
    }
    
 }

结果:主线程和线程池全是异步
在这里插入图片描述

 ============================================ 另一个例子 ============================================



@Component
public class TestTask {
    @Async("MyThreadPool")
    public void task1(){
        System.out.println("task1线程:"+Thread.currentThread().getName()+"---start--- "+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("task1线程:"+Thread.currentThread().getName()+"---end--- "+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
    }

    @Async("MyThreadPool")
    public Future<String> task2(){
        System.out.println("task2线程:"+Thread.currentThread().getName()+"---start--- "+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
        try {
            Thread.sleep(8000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //返回当前线程的线程名称
        System.out.println("task2线程:"+Thread.currentThread().getName()+"---end--- "+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
        return new AsyncResult<>(Thread.currentThread().getName());
    }
}
    @Autowired
    private TestTask testTask;
    
    @RequestMapping("test2")
    public String test2()  {
        System.out.println("主线程:"+Thread.currentThread().getName()+"---start--- "+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
        //异步获取子线程2返回数据
        Future<String> res = testTask.task2();
        //子线程1 无返回数据
        testTask.task1();
        // 主线程等子线程5s,5秒还没返回数据,主线程直接返回。
        for (int i = 5; i > 0; i--) {
            // 已完成
            if(res.isDone()){
                System.out.println("task2任务已经完成");
                try {  System.out.println("task2任务已经完成,返回值为:" + res.get());  }catch (Exception e){ System.out.println("获取异步内容失败!"); }
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) { e.printStackTrace();}
        }
        System.out.println("主线程:"+Thread.currentThread().getName()+"---end--- "+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));

        if(res.isDone()){
            return "success";
        }
        return "fail";
    }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值