1 前言
说到线程池,大家有没有想起“常量池”(字符串常量在Java程序最初创建的时候就已经准备好,等程序运行的时候,就已经加载到内存中了)呢?
线程池:把线程提前创建好,放到一个地方(类似于数组),需要用的时候随时去取,用完了还回池子中。
目的:让我们高效的创建销毁线程
有线程池的原因:最初引入线程是因为频繁创建销毁进程,太慢了。随着互联网的发展,随着我们对于性能要求的更进一步,咱们现在觉得频繁创建销毁线程,开始有些不能接受了。
那么为啥我们认为,直接创建线程的开销比从池子里取线程的开销大呢???
要想解释这个,就涉及到了操作系统的用户态和内核态。我们知道,一个操作系统 = 内核 + 配套的应用程序,内核包括操作系统的核心功能(1、管理硬件设备 2、给软件提供稳定的运行境),一个操作系统,一份内核,是要给所有的应用程序提供服务支持的。就相当于一个酒店的前台(内核)给所有的用户(应用程序)办理入住,假设用户可以自己办理入住哈(是不可能的),所以用户有两个选择办理入住:第一:自己办理,这个是完全可控的,第二:让酒店的前台帮我们办理,如果这时他没有处理其他事情,我们就可以马上办理,但是如果有其他事情要处理,我们就得等着他忙完之后再帮我们办理,这就不可控了。可以明显的看出第一个选择更高效。
如果有一段代码是由应用程序自行完成的,整个执行过程是可控的。如果有一段代码需要进入到内核中,由内核负责完成一系列操作,这个过程是不可控的,咱们程序员写的代码干预不了。因此,通常认为可控的过程比不可控的过程更高效。
从线程池取现成的线程是纯应用程序代码就可以完成的,是可控的。从操作系统创建新线程,就需要操作系统内核配合完成,是不可控的。使用线程池就可以省下应用程序切换到内核中这样的开销。
2 ThreadPoolExecutor
ThreadPoolExecutor:Java标准库里提供的直接使用的线程池,线程池里准备好一些线程,让这些线程执行一些任务。
核心方法:submit(Runnable),通过Runnable描述一段要执行的任务,通过submit 任务放到线程池中,此时线程池里的线程就会执行这样的任务。
构造这个类的时候,构造方法,有点麻烦(参数有点多),我们就来一个一个的学习吧~
2.1 参数
Java的线程池里面包含几个线程是可以动态调整的,任务多的时候,自动的扩容成更多的线程,任务少的时候,把额外的线程干掉,节省资源。
1)核心线程数 int corePoolSize:至少有多少个线程,线程池一创建,这些线程也要随之创建,直到整个线程池销毁,这些线程才会销毁。
2)最大线程数 int maximumPoolSize:核心线程 + 非核心线程(不繁忙就销毁,繁忙就创建)
3)long keepAliveTime:非核心线程允许的最大空闲时间(留一会非核心线程,先别销毁,万一又忙了呢)
4)时间单位 TimeUnit unit:枚举
5)工作队列 BlockingQueue<Runnable> workQueue:线程池本质上也是生产者消费者模型。调用submit就是在生产任务,把任务往这里添加,线程池里的线程就是在消费任务,从这里面取走元素并且执行Runnable里面的run方法,生产和消费之间就需要这样的队列传递数据
6)线程工厂 ThreadFactory threadFactory:工厂模式 (也是一种设计模式,和单例模式是并列的关系),用来弥补构造方法的缺陷的

参数的个数/参数类型 不同的
构造方法的名字是固定的,要想提供不同的版本,就需要重载,但是有时候就不一定能构成重载,所以使用工厂模式。
工厂方法的核心是通过静态方法,把构造对象new的过程,各种属性初始化的过程封装起来了。提供多组静态方法,实现不同情况的构造。
工厂方法:

提供工厂方法的类,就可以称为“工厂类”。

7)拒绝策略 RejectedExecutionHandeler handler:整个线程池七个参数中,最重要、最复杂的。submit把任务添加到任务队列中,任务队列是阻塞队列。对于线程池来说,入队操作时,发现队列满了,不会真的触发“入队列操作”,不会真阻塞,而是执行拒绝策略的相关代码。
四个类:
a)ThreadPoolExecutor.AbortPolicy:线程池直接抛出异常(线程池就可能无法继续工作)
b)ThreadPoolExecutor.CallerRunsPolicy:让调用submit的线程自行执行任务
submit方法有点门道:判断当前队列是否满,判断当前策略是否是CallerRunsPolicy
如果都是,submit内部就会直接调用Runnable
c)ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列中最老的任务
d)ThreadPoolExecutor.DiscardPolicy:丢弃队列中最新的任务,当前submit的这个任务
3 Executors
Java标准库提供了另一组类(Executors),针对ThreadPoolExecutor进行了进一步的封装,基于工厂设计模式,简化线程池的使用。
代码示例:

线程池里面有四个线程
表示的是固定数量的线程池:
核心线程数和最大线程数一样,不会变多,也不会变少

最大线程数是一个很大的数字(线程可以无限增加)
结果用
这个接收

模拟线程池的代码,请听下回分解~
OK啦,到此结束!!!




