为什么要引入线程池?
首先,对于进程来说,系统的创建和销毁进程的开销太大了, 因此解决方案就是进程池 或者 线程 ,
但是再进一步,线程虽然比进程轻了,但是如果创建和销毁的频率进一步的增加, 仍然会发现开销依旧很大, 因此就引入了线程池 这一方面的概念.
线程池:
线程池变快的原因
首先了解用户态和内核态
用户态:
自己的代码也就是最上面的应用程序 这一层来运行的,这里的代码统称为"用户态" 运行的代码.
内核态:
例如System.out.println方法 本质需要经过write系统调用, 进入内核中, 内核执行一堆逻辑,控制显示器输出字符串,
这种再内核宗运行的代码 统称为"内核态"代码
创建线程本身就需要内核的支持,(创建线程本质是再内核中搞一个PCB , 加到链表内)
调用Thread.start 其实归根结底 也是 要进入内核态来运行.
而把创建好的线程放进"池子"里面 由于池子是用户态实现的,放到这个池/从池子取 这个过程中不需要涉及到内核态,就是存粹的用户态代码就能完成.
一般认为 纯用户态的操作,效率要比经过内核态处理的操作效率要更高.
也就是线程池中的线程一直保存在里面 , 不会被内核回收,等待程序调用.
线程池的优点:
- 降低资源消耗:减少线程的创建和销毁带来的性能开销。
- 提高响应速度:当任务来时可以直接使用,不用等待线程创建
- 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。
线程池的使用
线程池的构造方法有四种:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler){
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
其中线程池的构造方法的参数有:
- int corePoolSize : 允许存在的核心线程数,即使这些线程没有被调用 , 也会被保留 不被销毁
- int maxnumPoolSize : 允许存在的最大线程数, 当核心线程来不及调用执行任务的时候, 会额外创建其他的线程,来进行任务的执行.
- long keepAliveTime : 临时创建出来的线程的存活时间, 时间一到这些临时创建出来的线程就会被销毁.
- TimeUnit unit : 指定 keepAliveTime 的单位
- BlockingQueue workQueue : **任务队列,**线程池会提供一个submit方法来进行提交任务到该任务队列里面, 线程池里面的线程就可以从这个工作队列里面拿取任务然后执行.
- ThreadFactory threadFactory : 创建线程的工厂方式
- RejectedExecutionHandler handler : 拒绝策略.
线程池的参数
线程池的拒绝策略 :
-
ThreadPoolExecutor.AbortPolicy
: 直接抛出一个RejectedExecutionException
异常ThreadPoolExecutor.CallerRunsPolicy
:哪个线程调用的这个任务, 那么就让该线程执行, 本身收到任务线程不执行该任务ThreadPoolExecutor.DiscardPolicy
: 丢弃最新任务,执行接收到的任务.ThreadPoolExecutor.DiscardOldestPolicy
: 丢弃最老任务, 执行收到的任务.
线程池的简化版本:
提供了另一种类 Excutors , 本质上是针对ThreadPoolExecutor进行了封装 ,提供了一些参数
public class Pools {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);
Executors.newCachedThreadPool();
Executors.newSingleThreadExecutor();
Executors.newScheduledThreadPool();
pool.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("hello threadPool");
}
}
});
}
}
- newFixedThreadPool : 创建了一个**固定线程数目的线程池,**参数制定了线程个数
- newCachedThreadPool : 创建了一个自动扩容的线程池,会根据任务量来自动进行扩容
- newSingleThreadExecutor : 创建了一个只有一个线程的线程池
- newScheduledThreadPool :创建了一个带有定时器功能的线程池 , 类似于Timer