ThreadPoolExecutor创建线程池

使用线程池的好处

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 提高响应速度。当任务到达时,任务可以不需要等到线程的创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。

源码参考

线程池的7大核心参数

public ThreadPoolExecutor(
    int corePoolSize,           // 核心工作线程(当前任务执行结束后,不会被销毁)
    int maximumPoolSize,        // 最大工作线程(代表当前线程池中,一共可以有多少个工作线程)
    long keepAliveTime,         // 非核心工作线程在阻塞队列位置等待的时间
    TimeUnit unit,              // 非核心工作线程在阻塞队列位置等待时间的单位
    BlockingQueue<Runnable> workQueue,   // 任务在没有核心工作线程处理时,任务先扔到阻塞队列中
    ThreadFactory threadFactory,         // 构建线程的线程工厂,可以设置thread的一些信息
    RejectedExecutionHandler handler) {  // 当线程池无法处理投递过来的任务时,执行当前的拒绝策略
    // 初始化线程池的操作
}

  • corePoolSize:核心线程数,队列任务未达到队列容量时,最大可以同时运行的线程数量
  • maximumPoolSize:最大线程数,队列任务达到队列容量时,最大可以同时运行的线程数量变为最大线程数
  • workQueue:新任务来的时候会判断当前运行的线程数量是否达到核心线程数,如果达到,新任务会被存放到队列中
  • keepAliveTime:线程池中的线程数量大于核心线程,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待时间超过keepAliveTime,才会回收销毁。
  • unit:keepAliveTime参数的单位
  • handler:饱和策略

饱和策略

当前同时运行的线程数量达到最大线程数,并且队列已经被放满任务时,触发拒绝策略。

  • AbortPolicy:【默认处理】,抛出 RejectedExecutionException 来拒绝新来的任务 ,代表你将丢失对这个任务的处理
  • CallerRunsPolicy:【推荐】,当前拒绝策略会在线程池无法处理任务时,由主线程去执行当前任务
  • DiscardPolicy:不处理任务,直接丢掉
  • DiscardOldestPolicy:将队列中最早的任务丢弃掉,将当前任务再次尝试交给线程池处理

  • 自定义policy策略,实现RejectedExecutionHandler

  private static class MyRejectedExecution implements RejectedExecutionHandler{
      @Override
      public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
          System.out.println("根据自己的业务情况,决定编写的代码!");
      }
  }

总结

当我们不指定 RejectedExecutionHandler 饱和策略的话来配置线程池的时候默认使用的是 ThreadPoolExecutor.AbortPolicy。在默认情况下,ThreadPoolExecutor 将抛出 RejectedExecutionException 来拒绝新来的任务 ,这代表你将丢失对这个任务的处理。 对于可伸缩的应用程序,建议使用 ThreadPoolExecutor.CallerRunsPolicy。当最大池被填满时,此策略为我们提供可伸缩队列。

构建线程池,并处理有无返回结果的任务

public static void main(String[] args) throws ExecutionException, InterruptedException {
    //1. 构建线程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
            2,
            5,
            10,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(5),
            new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r);
                    thread.setName("test-ThreadPoolExecutor");
                    return thread;
                }
            },
            new MyRejectedExecution()
    );

    //2. 让线程池处理任务,没返回结果
    threadPool.execute(() -> {
        System.out.println("没有返回结果的任务");
    });

    //3. 让线程池处理有返回结果的任务
    Future<Object> future = threadPool.submit(new Callable<Object>() {
        @Override
        public Object call() throws Exception {
            System.out.println("我有返回结果!");
            return "返回结果";
        }
    });
    Object result = future.get();
    System.out.println(result);

    //4. 如果是局部变量的线程池,记得用完要shutdown
    threadPool.shutdown();
}

private static class MyRejectedExecution implements RejectedExecutionHandler{
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println("根据自己的业务情况,决定编写的代码!");
    }
}

如何正确配置线程池参数

如何区分是CPU密集任务还是IO密集任务?

以下的N是指CPU核心数

IO密集型【推荐核心线程数2N】:涉及到网络读取、文件读取都属于IO密集;大部分时间都花费在处理IO交互上,在处理IO交互时间段内不会占用CPU来处理,这样CPU就可以交给其它线程来使用。

CPU密集型【推荐核心线程数N+1】:这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1。比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间

参考美团的动态调参

Java线程池实现原理及其在美团业务中的实践 - 美团技术团队

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ThreadPoolExecutorJava中的一个线程池类,可以用来管理和调度线程。它可以在需要时自动创建线程,并在不需要时回收线程,从而避免了频繁地创建和销毁线程的开销。ThreadPoolExecutor可以设置线程池的大小、任务队列的大小、线程池的拒绝策略等参数,以满足不同的需求。使用ThreadPoolExecutor可以提高程序的性能和稳定性,减少资源的浪费。 ### 回答2: ThreadPooExecutor是Java中的线程池框架,提供了一种方便的方式来管理线程,减少线程的创建和销毁次数,节约系统资源,同时提高程序的运行效率。 在使用ThreadPooExecutor创建线程池时,需要通过构造函数来初始化线程池的大小、线程的缓存队列、线程的生命周期等参数。其中,参数包括:corePoolSize(核心池大小),maximumPoolSize(最大池大小),keepAliveTime(线程池维护线程所允许的空闲时间),unit(计时单位)以及一个BlockingQueue(阻塞队列)。 当线程池中的线程数目超过了corePoolSize时,就会将任务加入BlockingQueue中,等待有空闲线程时执行。当BlockingQueue满时,就会创建新的线程执行任务。如此反复,直到线程池中的线程数目达到maximumPoolSize(最大线程池大小)。 当线程池中的线程数目超过了maximumPoolSize时,便会执行拒绝策略,对新的任务进行拒绝或抛出异常等处理。 在使用ThreadPooExecutor的时候,我们可以使用ThreadPoolExecutor类提供的execute()方法,将一个任务提交给线程池进行执行。线程池会自动将任务分配给其中的一个线程执行。任务完成后,线程会自动返回线程池,等待下一个任务的分配。 总的来说,ThreadPooExecutor是Java多线程编程中非常重要的工具,能够让我们更好地管理线程,提高程序的运行效率。但需要注意的是,当线程池的参数设置不当或者程序的运行流程不清晰时,可能会导致程序运行出错或者出现各种性能问题。因此,在使用ThreadPooExecutor创建线程池时,我们应该根据程序的实际需求来进行参数的设置和调整,保证线程池的高效和稳定运行。 ### 回答3: 线程池Java多线程编程中非常重要的一个概念,它可以避免线程频繁创建和销毁所带来的开销,提高程序的性能和稳定性。而ThreadpoolExecutorJava线程池的一个实现类,它可以通过一些参数的设置来调整线程池的大小,队列容量等属性,实现线程池的优化和控制。 ThreadpoolExecutor的主要构造函数有四个参数:corePoolSize,maximumPoolSize,keepAliveTime和unit。其中corePoolSize表示线程池中核心线程的数量,maximumPoolSize表示线程池中最多允许的线程数量,keepAliveTime表示没有任务时,多余的线程保留的时间,unit为保留时间的单位。 当线程数小于核心线程数时,会创建新的线程;当线程数大于核心线程数时,会把任务放入缓存队列中;当线程数大于最大线程数时,会触发线程池的拒绝策略,例如抛出异常或者忽略新任务等。 ThreadpoolExecutor中还有一个重要的属性——BlockingQueue队列,用来存储等待被执行的任务,同时它也是线程池的瓶颈所在。如果处理任务的速度太慢,缓存队列会变得越来越大,导致内存占用过多,甚至OOM。 因此,好的线程池应该根据任务类型的特性和负载情况设置合理的线程数和队列容量,避免线程池在高负载等情况下失控,同时也能提高程序的性能和稳定性。线程池是多线程编程中非常重要的一环,学习和掌握ThreadpoolExecutor的用法和优化方法将会对Java编程的水平和职业发展都有益处。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值