详谈线程池的理解和应用

正因为我抱着与你相见的但愿,我才永远认为最崎岖的路是最好的路。html

1、线程池的好处

线程池是啥子,干啥使它呀,老子线程使得好好的,非得屡次一举,哈哈,想必来这里看这篇文章的都对线程池有点了解。那么我来整理整理线程池的好处吧。面试

一、线程池的重用

线程的建立和销毁的开销是巨大的,而经过线程池的重用大大减小了这些没必要要的开销,固然既然少了这么多消费内存的开销,其线程执行速度也是日新月异的提高。设计模式

二、控制线程池的并发数

初学新手可能对并发这个词语比较陌生,特此我也是结合百度百科和必生所学得出最优解释,万万记着并发可跟并行不同。并发

并发:在某个时间段内,多个程序都处在执行和执行完毕之间;但在一个时间点上只有一个程序在运行。头脑风暴:老鹰妈妈喂小雏鹰食物,小雏鹰不少,而老鹰只有一张嘴,她须要一个个喂过去,到最后每一个小雏鹰均可以吃到,可是在一个时间点里只能有一个小雏鹰能够吃到美味的食物。异步

并行:在某个时间段里,每一个程序按照本身独立异步的速度执行,程序之间互不干扰。头脑风暴:这就好似老鹰妈妈决定这样喂食太费劲因而为每一个小雏鹰请了个保姆,这样子在一个时间点里,每一个小雏鹰均可以同时吃到食物,并且互相不干扰。this

回到线程池,控制线程池的并发数能够有效的避免大量的线程池争夺CPU资源而形成堵塞。头脑风暴:仍是拿老鹰的例子来说,妈妈只有一个,要这么一个个喂下去,一些饿坏的小雏鹰等不下去了就要破坏规则,抢在靠前喂食的雏鹰面前,而前面的雏鹰也不是吃软饭的,因而打起来了,场面混乱。老鹰生气了,这么不懂事,谁也别吃了,因而形成了最后谁也没食吃的局面。.net

三、线程池能够对线程进行管理

线程池能够提供定时、按期、单线程、并发数控制等功能。好比经过ScheduledThreadPool线程池来执行S秒后,每隔N秒执行一次的任务。线程

2、线程池的详解

推荐博客: http://blog.csdn.net/seu_calvin/article/details/52415337

想必看完上面那篇博客,你们可谓赞不绝口,不过可能有些小伙伴仍是记不下来,还有些小伙伴以为好恶心呀,怎么都是厕所啥的呀!哈哈别着急,我来给你们一种好记的办法。设计

先来说讲参数最多的那个构造方法,主要是对那几个烦人的参数进行分析。code

一、ThreadPoolExecutor

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

这里是7个参数(咱们在开发中用的更多的是5个参数的构造方法),OK,那咱们来看看这里七个参数的含义:

  • corePoolSize 线程池中核心线程的数量
  • maximumPoolSize 线程池中最大线程数量
  • keepAliveTime 非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime以后,则会被回收。若是ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长
  • unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等
  • workQueue 线程池中的任务队列,该队列主要用来存储已经被提交可是还没有执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。
  • threadFactory 为线程池提供建立新线程的功能,这个咱们通常使用默认便可
  • handler 拒绝策略,当线程没法执行新任务时(通常是因为线程池中的线程数量已经达到最大数或者线程池关闭致使的),默认状况下,当线程池没法处理新线程时,会抛出一个RejectedExecutionException。

emmmmm…看到那么多烦人的概念,是否是有点头大了,我反正是头大了。

这7个参数中,日常最多用到的是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue.在这里我主要抽出corePoolSize、maximumPoolSize和workQueue三个参数进行详解。

maximumPoolSize(最大线程数) = corePoolSize(核心线程数) + noCorePoolSize(非核心线程数)

(1)当currentSize<corePoolSize时,没什么好说的,直接启动一个核心线程并执行任务。

(2)当currentSize>=corePoolSize、而且workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。

(3)当workQueue已满,可是currentSize<maximumPoolSize时,会当即开启一个非核心线程来执行任务。

(4)当currentSize>=corePoolSize、workQueue已满、而且currentSize>maximumPoolSize时,调用handler默认抛出RejectExecutionExpection异常。

什么currentSize,corePoolSize,maximumPoolSize,workQueue比来比去的都比迷糊了,哈哈,那我举个烧烤店的例子来想必你们理解起来更快。

夏天了,很热,因此不少烧烤店都会在外面也布置座位,分为室内、室外两个地方能够吃烧烤。(室内有空调电视,并且室内比室外烧烤更加优惠,并且外面下着瓢泼大雨因此顾客会首先选择室内)

corePoolSize(烧烤店室内座位),currentPoolSize(目前到烧烤店的顾客数量),maximumPoolSize(烧烤店室内+室外+侯厅室全部座位),workQueue(烧烤店为顾客专门设置的侯厅室)

第(1)种,烧烤店人数很少的时候,室内位置不少,你们都其乐融融,开心的坐在室内吃着烧烤,看着世界杯。

第(2)种,生意不错,室内烧烤店坐无空席,你们都不肯意去外面吃,因而在侯厅室里呆着,侯厅室位置没坐满。

第(3)种,生意兴隆,室内、侯厅室都坐无空席,可是顾客太饿了,剩下的人没办法只好淋着雨吃烧烤,哈哈,好可怜。

第(4)种,生意爆棚,室内、室外、侯厅室都坐无空席,再有顾客过来直接赶走。

对于workQueue仍是有点陌生的小伙伴。

推荐博客: http://blog.csdn.net/u0127025...

二、其余线程池的记法

在这里主要是跟你们分享一种特别容易记住其余四种线程池的方法,在你们写代码,面试时能够及时想到这四种线程池。

(1)FixedThreadPool:

Fixed中文解释为固定。结合在一块儿解释固定的线程池,说的更全面点就是,有固定数量线程的线程池。其corePoolSize=maximumPoolSize,且keepAliveTime为0,适合线程稳定的场所。

(2)SingleThreadPool:

Single中文解释为单一。结合在一块儿解释单一的线程池,说的更全面点就是,有固定数量线程的线程池,且数量为一,从数学的角度来看SingleThreadPool应该属于FixedThreadPool的子集。其corePoolSize=maximumPoolSize=1,且keepAliveTime为0,适合线程同步操做的场所。

(3)CachedThreadPool:

Cached中文解释为储存。结合在一块儿解释储存的线程池,说的更通俗易懂,既然要储存,其容量确定是很大,因此他的corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE(2^32-1一个很大的数字)

(4)ScheduledThreadPool:

Scheduled中文解释为计划。结合在一块儿解释计划的线程池,顾名思义既然涉及到计划,必然会涉及到时间。因此ScheduledThreadPool是一个具备定时按期执行任务功能的线程池。

3、线程池的单例

什么是单例呢?咳咳。

一、单例

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种模式涉及到一个单一的类,该类负责建立本身的对象,同时确保只有单个对象被建立。这个类提供了一种访问其惟一的对象的方式,能够直接访问,不须要实例化该类的对象。

注意事项:

  • 单例类只能有一个实例。
  • 单例类必须本身建立本身的惟一实例。
  • 单例类必须给全部其余对象提供这一实例。
推荐: http://www.runoob.com/design-...

二、线程池的单例

那么问题来了,我线程池用的好好的,用的时候建立一个,不用就无论他,那为何要将线程池设计成单例模式呢。那么就要看看你将线程池应用的场所了。通常状况下,整个系统中只须要单种线程池,多个线程公用一个线程池,不会是每创一个线程就要建立一个线程池,那样子你还不如不用线程池呢。

言归正传,我们来看看如何将线程池设计成单例模式。废话少说上代码

首先在ThreadPool类里面实现线程池的建立,咱们这里建立的是FixedThreadPool线程池(记住构造方法要私有,保证不被其余类实例化)

private ThreadPool(int corepoolsize, int maximumpoolsize, long keepalivetime) {
    this.corepoolsize = corepoolsize;
    this.maximumpoolsize = maximumpoolsize;
    this.keepalivetime = keepalivetime;
}

public void executor(Runnable runnable) {
    if (runnable == null) {
        return;
    }
    if (mexecutor == null) {
        mexecutor = new ThreadPoolExecutor(corepoolsize, //核心线程数
                maximumpoolsize, //最大线程数
                keepalivetime, //闲置线程存活时间
                TimeUnit.MILLISECONDS, // 时间单位
                new LinkedBlockingDeque<Runnable>(), //线程队列
                Executors.defaultThreadFactory(), //线程工厂
                new ThreadPoolExecutor.AbortPolicy() //队列已满,并且当前线程数已经超过最大线程数时的异常处理策略
        );
    }
    mexecutor.execute(runnable);
}

再而后对ThreadPool内部类,在类里面对他实例化,实现单例

// 获取单例的线程池对象
public static ThreadPool getThreadPool() {
    if (mThreadPool == null) {
        synchronized (ThreadManager.class) {
            if (mThreadPool == null) {
                int cpuNum = Runtime.getRuntime().availableProcessors();// 获取处理器数量
                int threadNum = cpuNum * 2 + 1;// 根据cpu数量,计算出合理的线程并发数
                mThreadPool = new ThreadPool(threadNum, threadNum, 0L);
            }
        }
    }
    return mThreadPool;
}
相关文章
相关标签/搜索