(二)java.util.concurrent - Java 并发工具包 ---ThreadPoolExecutor创建线程池

目录

 

一,线程池简介

二,可以通过ThreadPoolExecutor来创建一个线程池,ThreadPoolExecutor类一共有4个构造方法。其中,拥有最多参数的构造方法如下所示:

三,线程池参数讲解:

 四 ,线程池的工作原理

五,线程池的使用示例代码如下:


一,线程池简介

  在编程中经常会使用线程来异步处理任务,但是每个线程的创建和销毁都需要一定的开销。如果每次
执行一个任务都需要开一个新线程去执行,则这些线程的创建和销毁将消耗大量的资源;并且线程都是“各
自为政”的,很难对其进行控制,更何况有一堆的线程在执行。这时就需要线程池来对线程进行管理。在
Java  1.5中提供了Executor框架用于把任务的提交和执行解耦,任务的提交交给Runnable或者Callable,而
Executor框架用来处理任务。Executor框架中最核心的成员就是 ThreadPoolExecutor,它是线程池的核心实现
类。本节就来着重讲解ThreadPoolExecutor

 


 

二,可以通过ThreadPoolExecutor来创建一个线程池,ThreadPoolExecutor类一共有4个构造方法。其中,拥有最多参数的构造方法如下所示:


public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

三,线程池参数讲解:

1.1 corePoolSize(必需):核心线程数。默认情况下线程池是空的,只有任务提交时才会创建线程。如果当前运
行的线程数少于corePoolSize,则创建一个核心线程来处理任务;如果等于或者多于corePoolSize,则不再创建。如
果调用线程池的prestartAllcoreThread方法,线程池会提前创建并启动所有的核心线程来等待任务默认情况下。核心线程会一直存活,但是当ThreadPoolExecutor调用allowCoreThreadTimeout方法设置为true时,核心线程也会在超时后被回收。

那么如何配置核心线程数?

(1)首先看下机器的CPU核数,然后在设定具体参数:

即CPU核数 = Runtime.getRuntime().availableProcessors()

2.分析下线程池处理的程序是CPU密集型,还是IO密集型

CPU密集型:核心线程数 = CPU核数 + 1

IO密集型:核心线程数 = CPU核数 * 2


1.2 maximumPoolSize(必需):线程池所能容纳的最大线程数。如果任务队列(workQueue)满了并且线程数小于
maximumPoolSize时,则线程池仍旧会创建新的线程来处理任务。如果任务队列(workQueue)未满,并且线程数小于
maximumPoolSize时,这时将任务放到任务队列(workQueue)中等待线程调度;


1.3 keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将allowCoreThreadTimeout设置为true时,核心线程也会超时回收。
unit(必需):指定keepAliveTime参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。


1.4 workQueue(必需):任务队列。通过线程池的execute()方法提交的Runnable对象将存储在该参数中。其采用阻塞队列实现。在Java中需要实现BlockingQueue接口。但Java已经为我们提供了7种阻塞队列的实现:

ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列(数组结构可配合指针实现一个环形队列)。
LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列,在未指明容量时,容量默认为Integer.MAX_VALUE。
PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列,对元素没有要求,可以实现Comparable接口也可以提供Comparator来对队列中的元素进行比较。跟时间没有任何关系,仅仅是按照优先级取任务。
DelayQueue:类似于PriorityBlockingQueue,是二叉堆实现的无界优先级阻塞队列。要求元素都实现Delayed接口,通过执行时延从队列中提取任务,时间没到任务取不出来。
SynchronousQueue: 一个不存储元素的阻塞队列,消费者线程调用take()方法的时候就会发生阻塞,直到有一个生产者线程生产了一个元素,消费者线程就可以拿到这个元素并返回;生产者线程调用put()方法的时候也会发生阻塞,直到有一个消费者线程消费了一个元素,生产者才会返回。
LinkedBlockingDeque: 使用双向队列实现的有界双端阻塞队列。双端意味着可以像普通队列一样FIFO(先进先出),也可以像栈一样FILO(先进后出)。
LinkedTransferQueue: 它是ConcurrentLinkedQueue、LinkedBlockingQueue和SynchronousQueue的结合体,但是把它用在ThreadPoolExecutor中,和LinkedBlockingQueue行为一致,但是是无界的阻塞队列。


有界队列和无界队列的区别:如果使用有界队列,当队列饱和时并超过最大线程数时就会执行拒绝策略;而如果使用无界队列,因为任务队列永远都可以添加任务,所以设置maximumPoolSize没有任何意义。

 

 

1.5 threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。

   不熟悉线程工厂的可以看这里线程工厂戳这里


1.6  handler(可选):拒绝策略。当任务队列和线程池都满了时所采取的应对策略,默认
是AbordPolicy,表示无法处理新任务,并抛出RejectedExecutionException异常。

当线程池的线程数达到最大线程数时,需要执行拒绝策略。拒绝策略需要实现RejectedExecutionHandler接口,并实现rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法。不过Executors框架已经为我们实现了4种拒绝策略:

AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常。
CallerRunsPolicy:由调用线程处理该任务。
DiscardPolicy:丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。
DiscardOldestPolicy:丢弃队列最早的未处理任务,然后重新尝试执行任务。
 

 四 ,线程池的工作原理

下面来描述一下线程池工作的原理,同时对上面的参数有一个更深的了解。其工作原理流程图如下:

 

 

 

 

① 向线程池中提交任务;

②当线程池中的线程数小于corePoolSize(核心线程数)时:新提交任务将创建一个核心线程执行任务,即使此时线程池中存在空闲线程。

③当线程池中的线程数大于corePoolSize(核心线程数)时,workQueue(任务队列)未添加满,将任务放入的任务队列中等待线程调度。
④当线程池中的线程数大于corePoolSize(核心线程数)时,workQueue(任务队列)已经满了,并且线程池中的线程数量小于maximumPoolSize(最大线程数)时:创建新的非核心线程来执行任务。

⑤当线程池中的线程数大于corePoolSize(核心线程数)时,workQueue(任务队列)已经满了,并且线程池中的线程数量大于maximumPoolSize(最大线程数)时:那么通过 handler所指定的策略来处理此任务。
⑥当线程池中非核心线程,空闲时间达到keepAliveTime时,关闭空闲线程
⑦当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭

比较清晰的流程图如下:

 

 

 

 

五,线程池的使用示例代码如下:

 


         // 创建线程池,最小线程数为2,最大线程数为4,线程池维护线程的空闲时间为3秒,
         // 使用有界队列,不指定深度,
         final RejectedExecutionHandler defaultHandler = new ThreadPoolExecutor.AbortPolicy();
        //1  创建线程池实例引用;
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 3, TimeUnit.SECONDS,  new LinkedBlockingQueue<Runnable>(),
                defaultHandler);



            // 2. 创建实现Runnable接口的 执行任务类
    private class MyRunnable  implements  Runnable{
        private  int index;
        public  MyRunnable(int i){
            index=i;
        }


        @Override
        public void run() {
            // 业务...执行的代码...省略
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.e("TAG","index="+index+"--当前线程:"+Thread.currentThread().getName() );

        }
    }





        for (int i = 0; i < 50; i++) {
            //
            MyRunnable myRunnable = new MyRunnable(i);
            //3 将将任务放入到线程池中;
            threadPoolExecutor.execute(myRunnable);

        }
        //指示当所有线程执行完毕后关闭线程池和工作线程,如果不调用此方法,jvm不会自动关闭
        threadPoolExecutor.shutdown();
        try {
            //shutdown配合shutdown关闭线程池 120秒之后关闭;
            threadPoolExecutor.awaitTermination(2*60,TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

运行结果:

2020-07-27 16:35:14.247 9117-9167/com.study.study E/TAG: index=0--当前线程:pool-1-thread-1
2020-07-27 16:35:14.247 9117-9168/com.study.study E/TAG: index=1--当前线程:pool-1-thread-2
2020-07-27 16:35:16.252 9117-9167/com.study.study E/TAG: index=3--当前线程:pool-1-thread-1
2020-07-27 16:35:16.252 9117-9168/com.study.study E/TAG: index=2--当前线程:pool-1-thread-2
2020-07-27 16:35:18.254 9117-9167/com.study.study E/TAG: index=4--当前线程:pool-1-thread-1
2020-07-27 16:35:18.254 9117-9168/com.study.study E/TAG: index=5--当前线程:pool-1-thread-2
2020-07-27 16:35:20.255 9117-9167/com.study.study E/TAG: index=6--当前线程:pool-1-thread-1
2020-07-27 16:35:20.256 9117-9168/com.study.study E/TAG: index=7--当前线程:pool-1-thread-2
2020-07-27 16:35:22.257 9117-9167/com.study.study E/TAG: index=8--当前线程:pool-1-thread-1
2020-07-27 16:35:22.258 9117-9168/com.study.study E/TAG: index=9--当前线程:pool-1-thread-2
2020-07-27 16:35:24.261 9117-9167/com.study.study E/TAG: index=10--当前线程:pool-1-thread-1

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值