首页 > 其他分享 >面试官:在项目中,你是如何使用线程池的?

面试官:在项目中,你是如何使用线程池的?

时间:2023-06-11 10:32:15浏览次数:37  
标签:面试官 项目 static 线程 new import public pool


大家好,我是田哥

前两天,有位星友(知识星球里的朋友简称)私信我,问在项目中如何使用线程池,关于线程池的原理和八股文相关的都可以背,但是要是问到你们项目中是怎么用的,心里总是有点慌。

公众号里回复77,获取面试小抄和面试相关资源:

话不多说,我们直接步入正题。

创建线程池的方式

我在这篇文章中聊过线程池相关的:

《阿里巴巴JAVA开发手册》有这样一条强制规定:线程池不允许使用Executors去创建,而应该通过ThreadPoolExecutor方式,这样处理方式更加明确线程池运行规则,规避资源耗尽风险。

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

上面这两种方式创建线程池使用的阻塞队列是LinkedBlockingQueue

/**
 * A constant holding the maximum value an {@code int} can
 * have, 2<sup>31</sup>-1.
 */
//2的31次方,然后在减1 2147483647
@Native public static final int   MAX_VALUE = 0x7fffffff;
public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}

不设大小理论上队列容量无上限,所以可能会堆积大量请求从而导致OOM

以上这种方式在,咱们就不聊了。

项目中如何用

在项目中,我们通常有两种方式创建线程池:

  • 第一种:静态方式
  • 第二种:使用Spring Boot创建线程池

比如说我们项目中需要处理用户登录日志,但是此时不想因为记录登录日志耽搁了登录。

如果我们使用同步的方式,可能会因为一些不太需要实时结果的,并且又耗时的业务可能会导致整个业务变慢:

面试官:在项目中,你是如何使用线程池的?_spring


耗时:200ms=100ms+100ms

如果使用线程池做了异步化后,直接创建个任务丢到线程池里,这样就减少了后面那100ms的等待时间。

面试官:在项目中,你是如何使用线程池的?_spring boot_02


在实际项目中,也有很多项目使用消息队列来做异步化,这个看项目情况来,比如:开发成本、后期运维成本等。

静态方式

静态方式就是当做一种工具类使用,代码实现如下:

package com.tianwc.myblog.pool;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//这里的相关只是一个演示,大家在定参数时,还是要以具体情况来
public class ThreadPoolUtil {
    //获取CPU核数
    static int cpuNums = Runtime.getRuntime().availableProcessors();
    /** 线程池核心池的大小*/
    private static int corePoolSize = 10;
    /** 线程池的最大线程数*/
    private static int maximumPoolSize = cpuNums * 5;
    //阻塞队列容量
    private static int queueCapacity = 100;
    //活跃时间,
    private static int keepAliveTimeSecond = 300;

    public static ExecutorService httpApiThreadPool = null;

    static{
        System.out.println("创建线程数:"+corePoolSize+",最大线程数:"+maximumPoolSize);
        //建立10个核心线程,线程请求个数超过20,则进入队列等待
        httpApiThreadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeSecond,
                TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(queueCapacity),new ThreadFactoryBuilder().setNameFormat("PROS-%d").build());
    }
}

关于线程池参数设置,可以参考:面试小抄中并发编程部分有详细说明。

在业务代码中的使用:

ThreadPoolUtil.httpApiThreadPool.submit(new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("======== 登录日志记录=====start=======");
                    try {
                        // TODO: 2022/4/14 业务处理
                        Thread.sleep(1000L);
                        System.out.println("userId=" + userId);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("========登录日志记录------end=======");
                }
            }));

很简单吧!

但是这种方式存在很多问题,很多项目也是这么在用的。

比如想动态修改线程池参数,这种方式就不好处理了

我们再来看看Spring Boot创建方式;

配置文件

我们可以把线程池相关参数配置在配置文件中application.yaml(application.properties)

threadpool:
  corePoolSize: 8
  maxPoolSize: 16
  queueCapacity: 5
  keepAliveSeconds: 300

然后创建线程池:

package com.tianwc.myblog.pool;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.annotation.Resource;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync //开启异步请求
public class ThreadPoolConfig {

    @Resource
    private Environment env;

    //创建线程池
    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
        pool.setThreadNamePrefix("--------------全局线程池-----------------");
        pool.setCorePoolSize(Integer.parseInt(env.getProperty("threadpool.corePoolSize")));
        pool.setMaxPoolSize(Integer.parseInt(env.getProperty("threadpool.maxPoolSize")));
        pool.setKeepAliveSeconds(Integer.parseInt(env.getProperty("threadpool.queueCapacity")));
        pool.setQueueCapacity(Integer.parseInt(env.getProperty("threadpool.keepAliveSeconds")));
        // 直接在execute方法的调用线程中运行
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        pool.initialize();
        return pool;
    }
}

我们在项目中使用:

package com.tianwc.myblog.pool;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class AsynchronousTask {

    @Async("taskExecutor")
    public void recordLoginLog(Long userId){
        System.out.println("======== 登录日志记录=====start=======");
        try {
            // TODO: 2022/4/14 业务处理
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("========登录日志记录------end=======");
    }
}

然后在登录的代码中使用:

@Resource
private AsynchronousTask asynchronousTask;

public Boolean login(){
    //登录处理
    Long userId=10001;
    boolean isSucc=true;
    if(isSucc){
        asynchronousTask.recordLoginLog(userId);
    }
    System.out.println("=====登录成功====");
}

输出日志:

=====登录成功====
======== 登录日志记录=====start=======
userId=10001
========登录日志记录------end=======

好了,以上就是我们项目中通常使用的方式,另外,注意,在项目中通常是将注解@EnableAsync 放到项目启动类上。

后记

关于线程池的实际使用,建议给大家看看美团的线程池技术方案,感兴趣的自己搜搜。

文中很多线程池相关的知识没有介绍,因为之前有一篇文章已经介绍过了,这里就不赘述了。


标签:面试官,项目,static,线程,new,import,public,pool
From: https://blog.51cto.com/u_11702014/6457147

相关文章

  • 使用GithubAction自动构建部署项目
    目录1.1项目准备2.1GithubAction设置3.1运行测试4.1小结GitHubActions是一种持续集成和持续交付(CI/CD)平台,可用于自动执行生成、测试和部署管道。您可以创建工作流程来构建和测试存储库的每个拉取请求,或将合并的拉取请求部署到生产环境。GitHubActions不仅仅是DevOps,还......
  • 202306-人民当家作组 实验七 综合软件项目案例
    项目内容课程班级博客链接2020级卓越工程师班这个作业要求链接实验七综合软件项目案例团队名称人民当家作组团队的课程学习目标(1)练习用例图、类图、顺序图、状态图等UML建模技术在软件开发过程中的用途;(2)掌握软件项目的数据库逻辑结构设计方法;(3)掌握软件项目......
  • 202307-什么是快乐星球组 实验七:综合软件项目案例
    项目内容课程班级博客链接2020级计算机科学与技术本次作业要求链接实验七:综合软件项目案例团队名称什么是快乐星球组团队成员分工描述张倩:任务三、任务四、任务六贾小萌:任务二、任务六、撰写博客葛薇:任务一、任务五、任务七、任务八团队的课程学习目标......
  • Java基础语法(二十):创建线程
    前言在计算机科学中,多线程是指在单个程序中同时执行多个线程。Java是一种支持多线程编程的语言,Java中的线程可以通过继承Thread类或实现Runnable接口来创建。本文将介绍Java多线程的基本概念和如何创建线程。介绍在Java中,线程是一种轻量级的进程,它可以与其他线程共享同一个进程的内......
  • SpringCloud项目中实现服务降级
    服务降级描述服务降级是服务自我保护的一种方式,或者保护下游服务的一种方式,用于确保服务不会受请求突增影响变得不可用,确保服务不会崩溃服务降级虽然会导致请求失败,但是不会导致阻塞。实现思路服务A使用Feign远程调用服务B。当服务A的访问量过大,服务B已无法支持服务......
  • ObjectARX 2014 项目升级到高版本vs2017出现提示平台集v141未安装
    ARX2014项目升级到vs2017的时候提示平台集未安装。解决方式:在vcproj文件中,添加相应的平台集。v141类似截图......
  • SpringCloud项目中实现服务降级
    服务降级描述服务降级是服务自我保护的一种方式,或者保护下游服务的一种方式,用于确保服务不会受请求突增影响变得不可用,确保服务不会崩溃服务降级虽然会导致请求失败,但是不会导致阻塞。实现思路服务A使用Feign远程调用服务B。当服务A的访问量过大,服务B已无法支持服务A的调用,......
  • 4、第一次构建项目报错处理
     翻译如下:编译错误此项目包含Java编译错误,可能导致自定义视图呈现失败。先修复编译问题。解决方案如下:找到File->InvalidateCaches/Restart清除缓存及重启Studio 点击InvalidateCaches/Restart清除缓存及重启Studio 解决了哦,没有报错了 ......
  • (2023.6.10)线程绑定到指定核上
    pthread_setaffinity_np与sched_setaffinity的区别:sched_setaffinity可在进程的线程中去修改亲和性写在启动脚本中是使用pthread_setaffinity_np、sched_setaffinity、还是tasklet?(https://www.cnblogs.com/x_wukong/p/5924298.html)c语言如何调用到系统命令reboot? 同时在......
  • 202303-天天向上队 实验七 综合软件项目案例
    项目内容课程班级博客链接2023年春软件工程这个作业要求链接实验七综合软件项目案例团队名称天天向上队团队的课程学习目标(1)练习用例图、类图、顺序图、状态图等UML建模技术在软件开发过程中的用途。(2)掌握软件项目的数据库逻辑结构设计方法。(3)掌握软件项目......