线程池的使用

目录

一、线程池概念

二、线程池重要参数

三、使用线程池的注意事项

四、使用场景及示例代码


一、线程池概念

从字面上理解它就是一个管理线程的池子。

  • 它帮我们管理线程,避免增加创建线程和销毁线程的资源损耗。因为线程其实也是一个对象,创建一个对象,需要经过类加载过程,销毁一个对象,需要走GC垃圾回收流程,都是需要资源开销的。
  • 提高响应速度。 如果任务到达了,相对于从线程池拿线程,重新去创建一条线程执行,速度肯定慢很多。
  • 重复利用。 线程用完,再放回池子,可以达到重复利用的效果,节省资源。

二、线程池重要参数

  • 核心线程数(corePoolSize):当线程池的线程都忙碌时,再进来新任务时,由最小线程数扩容到核心线程数 。一般设置为cpu的核数
  • 最大线程数 (maximumPoolSize):当核心线程数满了,当队列也满了,再来新任务,就会创建新非核心线程来执行这个新任务,直到线程数达到最大线程数。一般设置为cpu的核数*2
  • 存活时间(keepAliveTime):非核心线程 =(maximumPoolSize - corePoolSize ) ,非核心线程闲置下来不干活最多存活时间。当非核心线程超过多长时间不执行任务,处于空闲状态,线程池会将它回收,直到线程数量达到核心线程数。如果设置为0,就代表不回收
  • 存活时间单位(unit):线程池中非核心线程保持存活的时间的单位

TimeUnit.DAYS; 天
TimeUnit.HOURS; 小时
TimeUnit.MINUTES; 分钟
TimeUnit.SECONDS; 秒
TimeUnit.MILLISECONDS; 毫秒
TimeUnit.MICROSECONDS; 微秒
TimeUnit.NANOSECONDS; 纳秒

  • 线程池等待队列(workQueue):维护着等待执行的Runnable对象。当运行当线程数= corePoolSize时,新的任务会被添加到workQueue中,如果workQueue也满了则尝试用非核心线程执行任务,等待队列应该尽量用有界的。
  • threadFactory

        创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。

  • handler

        corePoolSize、workQueue、maximumPoolSize都不可用的时候执行的饱和策略。


三、使用线程池的注意事项

        线程池尽量不要使用的时候再创建,不要在业务逻辑里面去创建线程池,因为这样会在每次调用这个方法的时候,都会创建一个线程池。应该在项目启动的时候,就把线程池创建好。

四、使用场景及示例代码

1、任务来了,直接将任务交给线程池去做,至于成不成功、用不用返回值,不去关注。

 主要适用于一些更新操作,此操作不用过于关注执行结果或返回值。

线程池的使用_第1张图片

代码:起一个springboot项目

①写一个线程池配置类,加上@Configuration将线程池交给spring去管理

import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.*;

/**
 * 线程池配置
 *
 * @author yunyan
 * @date 2023/7/8
 */
@Configuration
public class ThreadPoolConfig {
    public static final String NAME_PRE="test";

    @Bean
    public static ExecutorService getExecutor(){
        return ExecutorBuilder.create()
                .setCorePoolSize(8)
                .setMaxPoolSize(16)
                .setKeepAliveTime(60, TimeUnit.SECONDS)
                .setWorkQueue(new LinkedBlockingQueue<>(2000))
                .setHandler(new ThreadPoolExecutor.CallerRunsPolicy())
                .setAllowCoreThreadTimeOut(false)
                .setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix(NAME_PRE).build())
                .build();
    }
}

②service层:

/**
 * 用户服务
 *
 * @author yunyan
 * @date 2023/5/29
 */
public interface IUserService extends IService {
    /**
     * 测试线程池
     */
    void testThreadPool();

}

③service实现类:

/**
 * 用户服务impl
 *
 * @author yunyan
 * @date 2023/5/29
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl implements IUserService {
    //通过Resource注解将ExecutorService 的实现类也就是ThreadPoolConfig 中getExecutor返回的线程池对象。
    @Resource
    private ExecutorService executorService;

    @Override
    public void testThreadPool(){
        log.info("我来啦");
        /**模拟业务场景耗时操作**/
        //交给线程池的线程去执行
        CompletableFuture.runAsync(()->{
            try {
                log.info("我真的来啦");
                Thread.sleep(10000);
                log.info("我是10s后执行的");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },executorService);

    }

④controller层:

 @GetMapping("/open/test")
    public ResultUtil test(){
        userService.testThreadPool();
        return ResultUtil.success(Boolean.TRUE);
    }

⑤结果

通过浏览器访问接口,不需要等待10s,结果直接返回浏览器。

线程池的使用_第2张图片

 2、任务来了,将任务交给线程池去执行,等任务都执行完了 ,主线程才能解除阻塞。

 适用于:

  • 自己的方法很复杂,需要多次查询数据库

线程池的使用_第3张图片

  • 需要循环调用http接口查询

  • 需要for循环查询数据库

①执行结果BO类

import lombok.Data;

/**
 * 查询
 *
 * @author yunyan
 * @date 2023/7/8
 */
@Data
public class QueryBO {

    private Boolean query1;
    private Boolean query2;
    private Boolean query3;
}

②线程池配置类

import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.*;

/**
 * 线程池配置
 *
 * @author yunyan
 * @date 2023/7/8
 */
@Configuration
public class ThreadPoolConfig {
    public static final String NAME_PRE="test-";

    @Bean("testThreadPool")
    public static ExecutorService getExecutor(){
        return ExecutorBuilder.create()
                .setCorePoolSize(8)
                .setMaxPoolSize(16)
                .setKeepAliveTime(60, TimeUnit.SECONDS)
                .setWorkQueue(new LinkedBlockingQueue<>(2000))
                .setHandler(new ThreadPoolExecutor.CallerRunsPolicy())
                .setAllowCoreThreadTimeOut(false)
                .setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix(NAME_PRE).build())
                .build();
    }
}

③service层:

/**
 * 用户服务
 *
 * @author yunyan
 * @date 2023/5/29
 */
public interface IUserService extends IService {
    /**
     * 测试线程池
     */
    void testThreadPool();

}

④service实现类

/**
 * 用户服务impl
 *
 * @author yunyan
 * @date 2023/5/29
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl implements IUserService {
    //通过Resource注解将ExecutorService 的实现类也就是ThreadPoolConfig 中getExecutor返回的线程池对象。
    @Resource
    private ExecutorService executorService;
    
    @Override
    public void testThreadPool(){
        log.info("我来啦");
        QueryBO queryBO=new QueryBO();

        //查看当前的时间戳
        long start = System.currentTimeMillis();
        CompletableFuture query1 = CompletableFuture.runAsync(() -> {
            Boolean aBoolean = null;
            try {
                aBoolean = query1();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            queryBO.setQuery1(aBoolean);
        }, executorService);

        CompletableFuture query2 = CompletableFuture.runAsync(() -> {
            Boolean aBoolean = null;
            try {
                aBoolean = query2();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            queryBO.setQuery2(aBoolean);
        }, executorService);

        CompletableFuture query3 = CompletableFuture.runAsync(() -> {
            Boolean aBoolean = null;
            try {
                aBoolean = query3();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            queryBO.setQuery3(aBoolean);
        }, executorService);

        /**将上面的三个任务放到数组中**/
        CompletableFuture[] completableFutures= Stream.of(query1,query2,query3).collect(Collectors.toList()).toArray(new CompletableFuture[3]);
        /**相当于阻塞的过程**/
        CompletableFuture.allOf(completableFutures).join();

        //查看时间差
        long time=System.currentTimeMillis()-start;
        log.info("查询结果{},时间是{}",queryBO,time);

    }

    public Boolean query1() throws InterruptedException {
        log.info("这是1");
        Thread.sleep(3000);
        return true;
    }
    public Boolean query2() throws InterruptedException {
        log.info("这是2");
        Thread.sleep(1000);
        return true;
    }
    public Boolean query3() throws InterruptedException {
        log.info("这是3");
        Thread.sleep(5000);
        return true;
    }

}

⑤controller

 @GetMapping("/open/test")
    public ResultUtil test(){
        userService.testThreadPool();
        return ResultUtil.success(Boolean.TRUE);
    }

⑥结果

通过调用接口可以发现三个任务是同时进行的,5s后,结果才返回给浏览器
通过日志可以观察到,这三个任务由原来串行化执行9s变为多线程并行执行5s

线程池的使用_第4张图片

你可能感兴趣的:(java,jvm,开发语言)