首页 > 其他分享 >线程池怎么用?--实例讲解

线程池怎么用?--实例讲解

时间:2023-08-02 18:48:02浏览次数:31  
标签:name -- text data 实例 线程 nodes type id

线程池使用实例

先写一个配置类

/**
 * 线程池配置
 */
@Configuration
public class ThreadPoolConfig {

    //定义线程前缀
     public static final String NAME_PRE="test";

    /**
     * ExecutorService 这个对象就是线程池,可以点进去他的源码看看
     * @Bean,将getExecutor()方法返回的对象交给Spring管理
     */
    @Bean
    public static ExecutorService getExecutor(){
        
        /**
         *方法一
         *ExecutorBuilder e=new ExecutorBuilder();
         *e.setAllowCoreThreadTimeOut(false);
        **/
        
        /**方法二直接Builder赋值**/
        return ExecutorBuilder.create()
                .setCorePoolSize(8)
                .setMaxPoolSize(16)
                .setKeepAliveTime(60, TimeUnit.SECONDS)
                .setHandler(new ThreadPoolExecutor.CallerRunsPolicy())
                .setWorkQueue(new LinkedBlockingDeque<>(2000))
                .setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix(NAME_PRE).build())
                .setAllowCoreThreadTimeOut(false)
                .build();
    }
}
  • ExecutorService 这个对象是线程池
  • @Bean,在项目启动时执行ExecutorService类的方法,返回一个对象交给spring管理。
  • @Configuration,配置注解,在项目启动时,首先执行这个类的代码。
  • 返回的对象第一种方法用set方法去赋值;第二种用Builder赋值。
 

参数内容为数字也通常为魔法值

方法1、可以放到配置文件中(yml配置)

threadpool:
  corepoolsize: 8
  maxPoolSize: 16
  # 队列大小
  dequesize: 2000
  # 线程前缀
  namepre: test-

方法2、设置静态常量

//定义线程前缀
public static final String NAME_PRE="test";

.setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix(NAME_PRE).build())
这样一个线程池就建好了  

写好了怎么用呢?

由于ExecutorService是一个interface,且交给了spring管理,所以直接注入使用。 这里在Impl注入。
@Resource
private ExecutorService executorService;

写一个测试尝试一下:

Service中写接口

/**Service中写接口**/

void test() throws InterruptedException;

 

Impl中重写方法(先模拟不用线程池)

使用@SentinelResource 注解 value指定资源的名称,blockHandler用于指定服务限流后的后续处理逻辑。 依赖:
<!--    如果要使用@SentinelResource必须添加此依赖    -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>1.8.1</version>
</dependency>
/**Impl中重写方法**/

@SentinelResource(value = "test",blockHandler="exceptionHandler")
@Override
public Boolean test() throws InterruptedException {
    /**模拟业务场景耗时**/
    Thread.sleep(100000);
    return true;
}

 

controller中调用接口

/**controller中调用接口**/

@GetMapping("/open/test")
public Boolean test() throws InterruptedException {
    userService.test();
    return Boolean.TRUE;
}
我们可以看到,由于睡眠操作,访问接口一直显示加载状态 0

 

怎么用线程池?两个方法

方法一 注解方式(用的不多)

 

方法二 使用CompletableFuture


@Override
public void test() throws InterruptedException {
    log.info("进来了");
    CompletableFuture.runAsync(()->{
        try {
            log.info("进来了1111");
            /**模拟业务场景耗时**/
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    },executorService);

}
浏览器访问会立即打开 0 后台日志也表明已将任务交给线程池 0   多执行几次看看 0 我们发现,线程池到 test7 后就没有了,因为我们设置的核心线程数为8,这8个线程都在帮我们干活,再多的线程任务将放置到队列中。 等一小会.... 发现刚刚被放入队列未执行的任务又被执行了,因为有线程执行完任务,就去队列中拿新任务去执行了。 0  

使用多线程的三种形式

1、将任务交给线程池去做,至于成不成功、需不需要返回值,不关注。   例如新增用户的三个任务交给线程池,不关注有没有成功。当主线程将所有任务交给线程池后,主线程就认为新增用户这个任务完成了,去进行下一项任务。 ===> 适用于更新操作,不关注返回值   2、将任务交给线程池去做,需要等待任务返回的结果。当主线程将任务交给线程池后,进入阻塞状态,直到获得所有的返回值。   例如将新增用户改为查询用户信息,我们知道查询一定是要返回结果的,所以主线程需要等待线程池内的任务执行完毕,他才能继续下一项任务,在这期间主线程一直处于阻塞状态。 ===> 适用于多IO操作,如:for循环查数据库、循环调用http接口查询、多次查询数据库操作  

模拟一下多线程查询

@Override
public void test() throws InterruptedException {
    log.info("进来了");
    
    //QueryBo为构建的返回值
    QueryBO queryBO=new QueryBO();
    
    //获取开始时间
    long start = System.currentTimeMillis();
    
    //创建线程,将任务放到线程池
    CompletableFuture<Void> query1 = CompletableFuture.runAsync(() -> {
        Boolean aBoolean = null;
        try {
            aBoolean = query1();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        queryBO.setQuery1(aBoolean);
    }, executorService);

    //创建线程,将任务放到线程池
    CompletableFuture<Void> query2 =CompletableFuture.runAsync(()-> {
        Boolean aBoolean = null;
        try {
            aBoolean = query2();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        queryBO.setQuery2(aBoolean);
    },executorService);

    //创建线程,将任务放到线程池
    CompletableFuture<Void> query3=CompletableFuture.runAsync(()-> {
        Boolean aBoolean = null;
        try {
            aBoolean = query3();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        queryBO.setQuery3(aBoolean);
    },executorService);
    
    //将这3个任务放到数组中
    CompletableFuture[] completableFutures=Stream.of(query1,query2,query3).collect(Collectors.toList()).toArray(new CompletableFuture[3]);
    
    //当数组里的任务都执行完,聚合一下,这行代码相当于将主线程阻塞住
    CompletableFuture.allOf(completableFutures).join();
    
    //打印时间戳
    long time = System.currentTimeMillis()-start;
    
    //allOf之后才会执行这句log,验证一下queryBO
    log.info("查询结果{},时间差{}",queryBO,time);
}

/**
 * 第一个查询,耗时3秒
 * @return
 * @throws InterruptedException
 */
public Boolean query1()throws InterruptedException{
    log.info("进来了1");
    Thread.sleep(3000);
    return true;
}

/**
 * 第二个查询,耗时1秒
 * @return
 * @throws InterruptedException
 */
public Boolean query2()throws InterruptedException{
    log.info("进来了2");
    Thread.sleep(1000);
    return true;
}

/**
 * 第三个查询,耗时5s
 * @return
 * @throws InterruptedException
 */
public Boolean query3()throws InterruptedException{
    log.info("进来了3");
    Thread.sleep(5000);
    return false;
}
给一个QueryBO,Controller、Service代码都是不变的,上边有
@Data
public class QueryBO {
    private Boolean query1;
    private Boolean query2;
    private Boolean query3;
}
运行一下子,看看结果: ===> 不出意料的时间差是5秒左右,QueryBO中也有三个任务的返回值,说明三个查询是一起执行的。 ===>重点代码是,以下两行,仔细研究
//将这3个任务放到数组中
CompletableFuture[] completableFutures=Stream.of(query1,query2,query3).collect(Collectors.toList()).toArray(new CompletableFuture[3]);

//当数组里的任务都执行完,聚合一下,这行代码相当于将主线程阻塞住
CompletableFuture.allOf(completableFutures).join();
 

线程不安全问题

定义一个全局变量count=1;每个线程内都去操作这个count,让它+1操作,最后count的结果是什么? 多执行几次结果: ===>发现这个count并不是连续递增,说明多线程操作一个变量是不安全的,可能会被互相覆盖。 ===>解决方法:加锁

标签:name,--,text,data,实例,线程,nodes,type,id
From: https://www.cnblogs.com/nliu/p/17601473.html

相关文章

  • 配置DHCP
    配置DHCP条件:关闭防火墙和selinux1,安装dhcp服务[root@localhost~]#yuminstalldhcp-y#安装dhcp服务2,查看配置文件[root@localhost~]#rpm-qcdhcp#查看配置文件2.1查看配置文件[root@localhost~]#vim/etc/dhcp/dhcpd.conf#空的##DHCPServerConfigurationfi......
  • Spring AOP
    springAOP基础知识AOP是什么AOP是一种变成思想,AOP全名AspectOrientProgramming,直译过来就是面向切面编程。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP简单的一点的理解就是,不改变源代码的......
  • 最新的iOS应用上架App Store详细流程解析
    最新的iOS应用上架AppStore详细流程解析2023已经过了2/3的时间,由于现在苹果签名市场的价格不断的上升,现在很多的开发商一直在想着如何进行上架一些自己的产品,下面小编来给大家梳理一下上架苹果市场的流程: 2020最新整理iOSapp上架app详细教程 上架iOS需要一个付费688的开......
  • Spring Boot中过滤器
    SpringBoot中过滤器过滤器是什么Filter也称之为过滤器,过滤器是对数据进行过滤,预处理。开发人员可以对客户端提交的数据进行过滤处理,比如敏感词,也可以对服务端返回的数据进行处理。还有就是可以验证用户的登录情况,权限验证,对静态资源进行访问控制,没有登录或者是没有权限时是不......
  • Spring Boot中的拦截器
    SpringBoot中的拦截器什么时拦截器SpringBoot中使用拦截器在SpringBoot中,我们可以通过拦截器(Interceptor)对控制器方法的执行进行拦截,实现预处理和后处理的功能。常见的用途有:日志记录、权限校验、性能监控等。实现一个拦截器需要实现HandlerInterceptor接口,该接口有......
  • nginx1.20-tomcat9-redisson集群,好像不怎么完善
    配置信息在同一台服务器上使用nginx做反向代理与两个tomcat组成简易tomcat集群使用nginx端口80tomcat1端口21005,21080,21009tomcat2端口22005,22080,22009配置过程1.先下载apache-tomcat-9.0.78,解压到两个目录,分别为tomcat1和tomcat2根据上方配置信息分别对两个目录中的......
  • 怀念中的java
     学了这门语言后一直没能做成项目,倒是安装环境,用记事本编辑的话,除了js最好做的就是java了。 以前学java的时候是一帮很有朝气的同学,在一个培训班,每天苦哈哈。从c开始学的语言,学完基础部分转入java。引入面向对象。后来引发了自己对面向对象长达好多好多年的思考。 老师说......
  • Linux shell 脚本中 if 的 “-e,-d,-f “ 说明
    1、文件表达式 2、整数变量表达式参数说明-eq等于-ne不等于-gt大于-ge大于等于-lt 小于-le小于等于 3、字符串变量表达式参数说明$a=$......
  • 8.2 后记
    T1简单的最短路到终点时不用等红灯,不然会挂40ptT2记\(f(i,j)\)表示跳到\((i,j)\)最少使用的体力。那么转移就是枚举上一个位置然后加上曼哈顿距离求最小值。考虑优化,我们注意到如果转移都在左上的话坐标正负的贡献是固定的,所以可以使用数据结构维护。先按照一维扫描线......
  • 老杜 JavaWeb 讲解(十七) ——JSP补充
    (十六)JSP补充相关视频:49-JSP的page指令以及九大内置对象和EL表达式指令指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)指令包括哪些呢?include指令:包含指令,在JSP中完成静态包含,很少用了。(这里不讲)taglib指令:引入标签库的指令。这个到JS......