Java线程池的四种实现方法及实现原理及分析。

原创 2018年04月15日 18:12:23

在阅读了《Android开发进阶,从小工到专家》的3.2.4.0~3.2.2.0 内容后启发很大,所以写了一篇博客总结一下关于线程池的部分内容。 


0.首先什么是线程池?

线程池就是创建多个线程并且进行管理的容器(线程池是个容器,可以创建线程和管理线程,并且给线程分配任务)


1.为什么要用线程池呢?

我们都知道,在Java中创建一个线程其实是一个很简单的事情,只要new Thread就可以了,但是这样做并不是一种很好的方式。那么为什么不好呢?

比如在一个项目里,全部都是用的new Thread的方式去启用线程,那么创建好Thread1,而1在运行的时候,创建了Thread2,等等等... 创建了10个线程的时候,

1,2,3都执行完毕了但是没被销毁,就可能导致无限制的新建线程,相互竞争,占用过多的系统资源,导致死锁以及OOM。

而且这些线程缺乏统一的管理的功能,也缺乏定期执行,定时执行,线程中断的功能。

说了这么多,那么线程池的好处是啥啊?

线程池的好处有以下3方面:

(1) 重用已经存在的线程,减少了线程的创建和销毁的开销。

(2)可有效控制最大并发的线程数,提高了系统资源的使用率避免很多竞争,避免了OOM啊 死锁啊等。

(3)可以提供定时和定期的执行方式,单线程,并发数量的控制等功能!

线程池可以使得对线程的管理更加方便,并且对高并发的控制尽在掌握。


2.四种Java线程池功能及分析

线程池都继承了ExecutorService的接口,所以他们都具有ExecutorService的生命周期方法:运行,关闭,终止;

因为继承了ExecutorService接口,所以它在被创建的时候就是处于运行状态,当线程没有任务执行时,就会进入关闭状态,只有调用了shutdown()的时候才是正式的终止了这个线程池。

java通过Executors工厂类提供我们的线程池一共有4种:

fixedThreadPool() //启动固定线程数的线程池

CachedThreadPool() //按需分配的线程池

ScheduledThreadPoolExecutor()//定时,定期执行任务的线程池

ThreadPoolExecutor()//指定线程数的线程池。


我们先来说参数最多的线程,方便下面对参数的理解,

ThreadPoolExecutor()

ThreadPoolExecutor是线程池的实现类之一,它主要是启动指定数量线程以及将任务添加到队列,并且将任务发送给空闲的线程。

我们来看一下拥有最多构造函数的线程池的构造函数吧:

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

corePoolSize : 线程的核心线程数。

maximumPoolSize:线程允许的最大线程数。

keepAliveTime:当前线程池 线程总数大于核心线程数时,终止多余的空闲线程的时间。

Unit:keepAliveTime参数的时间单位

workQueue:队伍队列,如果线程池达到了核心线程数,并且其他线程都处于活动状态的时候,则将新任务放入此队列。

threadFactory:定制线程的创建过程

Handler:拒绝策略,当workQueue队满时,采取的措施。

ThreadPoolExecutor()的作用主要是让我们更灵活的使用线程池,这里不详细的举例了。



fixedThreadPool() //启动固定线程数的线程池

在Android中,由于系统资源有限,所以我们最常用的就是fixedThreadPool()。

fixedThreadPool(int size) 就只有一个参数,size,就是线程池中最大可创建多少个线程

我们来看一下该线程池的实现过程


在实现上跟ThreadPoolExecutor类似fixedThreadPool简化了实现过程,把corePoolSize和maximumpoolSize的值都设为传入的size,并且设置keepAliveTime为0ms,然后采用的是LinkedBlockingQueue队列,这个队列是链式结构,所以是无边界的。可以容纳无数个任务。

总结:比如我创建3个线程的fixedThreadPool ,当3个都为活跃的时候,后面的任务会被加入无边界的链式队列(LinkedBlockingQueue),有空闲,就执行任务。

使用过程:

private static void fixedThreadPool(int size) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(size);
//        ExecutorService executorService2 = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            Future<Integer> task = executorService.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    System.out.println("执行线程" + Thread.currentThread().getName());
                    return fibc(40);
                }
            });
            System.out.println("第"+i+"次计算,结果:"+task.get());
        }

结果:



CachedThreadPool() //按需分配的线程池

对比fixedThreadPool来说 CachedThreadPool就要更快一些,为什么快呢?

我们来看一下CachedThreadPool的实现源码:


可以看到,这里的  maximumPoolSize:线程允许的最大线程数。  为MAX_VALUE(无限大),

也就是说,只要有任务并且其他线程都在活跃态,就会开启一个新的线程 (因为没有上限)

而当有空闲的线程的时候,就会去调用空闲线程执行任务。


使用过程:

 private static void newCacheThreadPool(){
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            Future task = executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"---->"+fibc(20));
                }
            });
        }
    }

结果:




ScheduledThreadPoolExecutor() 定时定期的线程池


创建ScheduledThreadPoolExecutor很简单,只要传入corePoolSize()线程的核心线程数。就可以开启这个线程池

然后我们需要使用它的定时任务,就需要实现他的scheduleAtFixedRate方法 这个方法有4个参数:


第一个为要执行的任务。

第二个为每次任务执行的延迟,比如传入1,就会每隔1秒执行一次。

第三个为执行的周期

第四个为第二个参数的时间单位。


*无论实现几个scheduleAtFixedRate方法,他们都互不干扰。


实现如下

private static void newScheduledThreadPool(){
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"----->" + fibc(40));
            }
        },5,3,TimeUnit.SECONDS);

        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"----->" + fibc(5));
            }
        },1,1,TimeUnit.SECONDS);


        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"----->" + fibc(1));
            }
        },1,2,TimeUnit.SECONDS);
    }

我这里实现了3个scheduleAtFixedRate方法,

第一个为5s一次,3个周期执行一次

第二个为1s一次,1个周期执行一次

第三个为1s一次,2个周期执行一次


结果如下:




总结:线程池的使用,可以更规范的管理和创建销毁线程,也可以更多样化的去使用线程,减小我们的系统开支。

Java并发(4)深入分析java线程池框架及实现原理(一)

先说说我个人对线程池的理解:线程池顾名思义是一个装有很多线程的池子,这个池子维护着从线程创建到销毁的怎个生命周期以及线程的分配,用户只需要把任务提交给这个线程池,线程池会自己给这些任务分配线程资源来完...
  • travellersY
  • travellersY
  • 2017-08-08 17:32:14
  • 592

Java线程池原理及四种线程池的使用

Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFi...
  • honghailiang888
  • honghailiang888
  • 2016-06-16 11:36:04
  • 4661

Java 四种线程池的用法分析

介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用,本文是基础篇。转载请标注原地址:http://blog.csdn.net/u011974987/article/de...
  • u011974987
  • u011974987
  • 2016-03-31 16:34:04
  • 11941

Java 并发编程深入学习——线程池及其实现原理

Java线程池介绍   线程池,从字面含义来看,是指管理一组同构工作线程的资源池。线程池是与工作队列(work Queue)密切相关的,其中工作队列中保存了所有等待执行的任务。工作者线程(Work ...
  • ylyg050518
  • ylyg050518
  • 2016-09-07 23:26:16
  • 1488

线程池原理(讲的非常棒)

Java并发编程:线程池的使用   在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:   如果并发的线程数量很多,并且每个线程都是执行一个时间...
  • gol_phing
  • gol_phing
  • 2015-10-10 23:01:21
  • 13673

Java中的线程池——ThreadPoolExecutor的原理

1 线程池的处理流程 向线程池提交一个任务后,它的主要处理流程如下图所示 一个线程从被提交(submit)到执行共经历以下流程: 线程池判断核心线程池里是的线程是否都在执行任务,如果不是...
  • u010723709
  • u010723709
  • 2015-12-22 19:59:42
  • 3866

我眼中的java线程池实现原理

最近在看java线程池实现方面的源码,在此做个小结,因为网上关于线程池源码分析的博客挺多的,我也不打算重复造轮子啦,仅仅用纯语言描述的方式做做总结啦!         个人认为要想理解清楚java线程...
  • hzw19920329
  • hzw19920329
  • 2016-08-30 19:41:27
  • 5965

java并发编程—— 线程池原理 详解 ThreadPoolExecutor

为什么要使用线程池 降低资源消耗: 通过重复利用线程,减少线程的创建销毁损耗的资源 提高响应速度: 任务到达时,不用重新创建线程,之间可以使用已经创建好的线程执行 提高线程的可管理性 线程池实现原...
  • lemon89
  • lemon89
  • 2016-03-26 17:42:19
  • 1922

java 线程池原理及几种线程池详解

java 线程池原理及几种线程池详解 1、为什么要用线程池? 服务器经常出现处理大量单个任务处理的时间很短而请求的数目却是巨大的请求。 构建服务器应用程序的一个过于简单的模型应该是:每当一个请求...
  • andychen314
  • andychen314
  • 2016-03-15 17:27:56
  • 3302
收藏助手
不良信息举报
您举报文章:Java线程池的四种实现方法及实现原理及分析。
举报原因:
原因补充:

(最多只允许输入30个字)