如果想了解线程池,首先要明白为什么要有线程池这个东西。
多线程是解决并发编程的方案,但是进程有点太重量了(创建和销毁,开销比较大)
因此引入了线程,线程比进程要轻量很对。即便如此,假如在某些场景中,需要频繁创建销毁线程,此时,线程的创建销毁和开销,也就不能忽视了。
为了解决这样的问题:
合理的使用线程池能带来3个好处
降低资源消耗,通过重复利用已经创建的线程降低线程创建和销毁造成的消耗提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行提高线程的可管理性,线程是稀缺资源,如果无限制地创建,不仅会消耗资源,还会降低系统稳定性,使用线程池可以统一分配、调优和监控。 如果是真正的创建或者销毁线程那就涉及到用户态和内核态的切换。
如果只是把线程放到线程池,就相当于全在用户态,在用户态效率就会更高。
ThreadPoolExecutor用户态:应用程序执行的代码
内核态:操作系统执行的代码
操作系统内核是很忙的,如果把进程某个任务交给操作系统去做,此时这个任务什么时候做完就很难把握了
ThreadPoolExecutor是Java标准库提供的线程池,它的构造方法的参数也是比较复杂的,来看一下它的构造方法的参数都是什么意思吧。
这是 ThreadPoolExecutor 的构造方法
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue
ThreadPoolExecutor里面包含的线程的数量并不是一成不变的,能够根据任务量自适应,如果任务比较多,就会多创建一些线程,如果任务比较少,就少创建一些线程。
corePoolSize :核心线程数(正式工)
当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建新的线程,等到需要执行的任务数量大于线程池的基本大小时就不再创建。如果调用了线程池的 prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程
maximumPoolSize:线程池的最大线程数(正式工+临时工)
如果队列满了,并且以创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。需要注意的是,如果使用了无界的任务队列这个参数就没有什么效果了。
如果我们要解决的任务场景任务量比较稳定,就可以设置 corePoolSize 和 maximumPoolSizejing 尽量接近一些(临时工就可以尽量少一些)
相反如果要解决的任务场景,任务量波动比较大,就可以设置 corePoolSize 和 maximumPoolSize相差更大一些(临时工就可以多一些)
这两者的值设置多少并不好说,要根据机器和任务场景通过实验的方式来确定。
keepAliveTime:线程活动保持时间(描述临时工可以摸鱼可以摸鱼多长时间)
线程池的工作线程空闲后,保持存活的时间。这个keepAliveTime时间越短,其实就是越希望吃的资源能更少。
TimeUnit unit:keepAliveTime的时间单位(ms,s,minute)
可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一秒)和纳秒(千分之一微秒)
workQueue:阻塞队列,就组织了线程池要执行的任务
可以选择好几种阻塞队列
ArrayBlockingQueue:基于数组的有界阻塞队列
linkedBlockingQueue:基于链表的阻塞队列,静态的工厂方法Executors.newFixedThreadPool ()使用了这个队列
SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。
PriorityBlockingQueue:一个具有优先级的无界阻塞队列。
threadFactory:线程的创建参数,来设定不同的线程的创建方式
用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
RejectedExecutionHandler handler
Executors拒绝策略(当任务队列满了的时候,又来了新任务)丢弃最新或者最老任务,阻塞等待、抛出异常等
由于ThreadPoolExecutor使用起来比较复杂,标准库又提供了一组其他的类,相当于对ThreadPoolExecutor又进行了一层封装。
标准库中提供了一个 Executors这个类,这个类相当于一个“工厂类”,通过这个类提供的一组工厂方法,就可以创建出不同风格的线程池实例了。
ExecutorService executorService = Executors.newCachedThreadPool();
newFixedThreadPool: 创建固定线程数的线程池 (完全没有临时工的版本)newCachedThreadPool: 创建线程数目动态增长的线程池 (完全没有正式员工,全是临时工)newSingleThreadExecutor: 创建只包含单个线程的线程池.(在特定场景会使用)newScheduledThreadPool: 能够设定延时时间的线程池(插入的任务能够过一会再执行)、相当于进阶版的Timer.Executors就是ThreadPoolExecutor类的封装,通过工厂模式直接调用工厂方法来创建实例。
ThreadPoolExecutor 和 Executors 一个简单一个复杂,要根据任务场景来决定用哪个。简单问题用简单解法,复杂问题用复杂解法。