乐趣区

关于后端:详谈线程池的理解和应用

正因为我抱着与你相见的心愿,我才永远认为最起伏的路是最好的路。

一、线程池的益处

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

1、线程池的重用

线程的创立和销毁的开销是微小的,而通过线程池的重用大大减少了这些不必要的开销,当然既然少了这么多生产内存的开销,其线程执行速度也是突飞猛进的晋升。

2、控制线程池的并发数

初学老手可能对并发这个词语比拟生疏,特此我也是联合百度百科和必生所学得出最优解释,万万记着并发可跟并行不一样。

并发:在某个时间段内,多个程序都处在执行和执行结束之间;但在一个工夫点上只有一个程序在运行。头脑风暴:老鹰妈妈喂小雏鹰食物,小雏鹰很多,而老鹰只有一张嘴,她须要一个个喂过来,到最初每个小雏鹰都能够吃到,然而在一个工夫点里只能有一个小雏鹰能够吃到美味的食物。

并行:在某个时间段里,每个程序依照本人独立异步的速度执行,程序之间互不烦扰。头脑风暴:这就好似老鹰妈妈决定这样喂食太吃力于是为每个小雏鹰请了个保姆,这样子在一个工夫点里,每个小雏鹰都能够同时吃到食物,而且相互不烦扰。

回到线程池,控制线程池的并发数能够无效的防止大量的线程池抢夺 CPU 资源而造成梗塞。头脑风暴:还是拿老鹰的例子来讲,妈妈只有一个,要这么一个个喂上来,一些饿坏的小雏鹰等不上来了就要毁坏规定,抢在靠前喂食的雏鹰背后,而后面的雏鹰也不是吃软饭的,于是打起来了,局面凌乱。老鹰怄气了,这么不懂事,谁也别吃了,于是造成了最初谁也没食吃的场面。

3、线程池能够对线程进行治理

线程池能够提供定时、定期、单线程、并发数管制等性能。比方通过 ScheduledThreadPool 线程池来执行 S 秒后,每隔 N 秒执行一次的工作。

二、线程池的详解

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

想必看完下面那篇博客,大家堪称拍案叫绝,不过可能有些小伙伴还是记不下来,还有些小伙伴感觉好恶心呀,怎么都是厕所啥的呀!哈哈别着急,我来给大家一种好记的方法。

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

1、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…

2、其余线程池的记法

在这里次要是跟大家分享一种特地容易记住其余四种线程池的办法,在大家写代码,面试时能够及时想到这四种线程池。

(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 是一个具备定时定期执行工作性能的线程池。

三、线程池的单例

什么是单例呢?咳咳。

1、单例

单例模式(Singleton Pattern)是 Java 中最简略的设计模式之一。这种模式波及到一个繁多的类,该类负责创立本人的对象,同时确保只有单个对象被创立。这个类提供了一种拜访其惟一的对象的形式,能够间接拜访,不须要实例化该类的对象。

注意事项:

  • 单例类只能有一个实例。
  • 单例类必须本人创立本人的惟一实例。
  • 单例类必须给所有其余对象提供这一实例。

举荐:http://www.runoob.com/design-…

2、线程池的单例

那么问题来了,我线程池用的好好的,用的时候创立一个,不必就不论他,那为什么要将线程池设计成单例模式呢。那么就要看看你将线程池利用的场合了。个别状况下,整个零碎中只须要单种线程池,多个线程专用一个线程池,不会是每创一个线程就要创立一个线程池,那样子你还不如不必线程池呢。

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

首先在 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;
}
退出移动版