Java线程池(1)——基本使用

引言

在阿里巴巴Java开放手册中有这么一条:

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样
的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

如果是不清楚Java线程池基本结构的同学看到这条强制约定可能会感到云里雾里。而本文将通过介绍Java线程池的基本使用与结构,让同学们能够抓住其中的来龙去脉。

概述

首先我们要明确的是,线程的创建和销毁是有代价的,因此往往我们要引入池化技术,也就是所说的线程池,它的特点有:

  • 重用线程池中的线程,减少因对象创建,销毁所带来的性能开销;
  • 能有效的控制线程的最大并发数,提高系统资源利用率,同时避免过多的资源竞争,避免堵塞;
  • 能够多线程进行简单的管理,使线程的使用简单、高效。

注意本文主要探讨的是Jav线程池的基本使用,如果想了解基本原理的可以移步啊啊

基本结构

java中的线程池是通过Executor框架实现的,Executor 框架包括类:Executor,Executors,ExecutorService,ThreadPoolExecutor ,Callable和Future、FutureTask的使用等。

如下图:

Executor: 所有线程池的接口,只有一个方法,就是执行命令。

public interface Executor {
    void execute(Runnable command);
}

ExecutorService: 增加Executor的行为,是Executor实现类的最直接接口。

ThreadPoolExecutor:线程池的具体实现类,一般用的各种线程池都是基于这个类实现的。
构造方法如下:

ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue,
                        ThreadFactory threadFactory,
                        RejectedExecutionHandler handler)

构造方法参数说明:

  • corePoolSize:核心线程数。默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。
  • maximumPoolSize:线程池所能容纳的最大线程数
  • keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收。
  • unit:指定keepAliveTime的单位,如TimeUnit.SECONDS。
  • workQueue: 线程池中用于存放任务的队列.
  • threadFactory:线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个newThread方法,通过线程工厂可以对线程的一些属性进行定制。.
  • RejectedExecutionHandler:当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略.默认策略是丢弃任务并抛出RejectedExecutionException异常。

通常有四种拒绝策略:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

Executors: 提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService 接口。

线程池的工作流程

  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

  2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:

  3. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
    如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
    如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

  4. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会采取拒绝策略。

  5. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
    6.当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

工具类Executors创建线程池

现在我们在来分析下阿里巴巴提到的几个创建线程的方法

newFixedThreadPool:有核心线程的线程池,大小固定 (其缓冲队列是无界的) 。

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

SingleThreadExecutor:单个后台线程 (其缓冲队列是无界的)。

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

可以看到以上的两种线程池都是使用LinkedBlockingQueue作为任务队列,而这种队列是无界的,因此阿里巴巴Java开发手册才会解释说:

FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

让我们继续看另外两种。

CachedThreadPool:无界线程池,可以进行自动线程回收。

public static ExecutorService newCachedThreadPool() {         
    return new ThreadPoolExecutor(0,Integer.MAX_VALUE,                                           
           60L, TimeUnit.SECONDS,                                       
           new SynchronousQueue<Runnable>());     
}

ScheduledThreadPool:核心线程池固定,大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

public static ExecutorService newScheduledThreadPool(int corePoolSize) {         
    return new ScheduledThreadPool(corePoolSize, 
              Integer.MAX_VALUE,                                                  
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,                                                    
              new DelayedWorkQueue());    
}

可以看到这两种线程池指定的maximumPoolSize都是Integer.MAX_VALUE,因此阿里巴巴Java开发手册才会解释说:

CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值