2、线程池的创建
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize: 线程池维护线程的最少数量 (core : 核心)
核心池的大小,默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,
就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达
的任务放到缓存队列当中。
maximumPoolSize: 线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
表示线程没有任务执行时最多保持多久时间会终止。 注意 默认情况下,只有当线程池中的
线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于
corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间
达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略
3、ThreadPoolExcutor内部执行流程
当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
也就是:处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue常用的是:java.util.concurrent.ArrayBlockingQueue
handler有四个选择:
ThreadPoolExecutor.AbortPolicy(): 抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy(): 重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy(): 抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy(): 抛弃当前的任务
1》创建固定个数的线程池
ExecutorService es = Exectors.newFixedThreadPool(10);
2》创建带缓存的线程池
ExecutorService es = Executors.newCachedThreadPool();
使用场景:短期有大量任务
3》创建可以执行定时任务的线程池
//创建执行定时任务的线程池
ScheduleExecutorService ses = Executors.newScheduledThreadPool(2);
//执行定时任务
ses.scheduledAtFixdRate(new Runnable(){
Public void run(){
}
},1,3,TimeUnit.SECONDS);
ses.schedule()只会执行一次
ses.scheduleWithFixedDelay() VS ses.scheduledAtFixdRate()
前者:以上一次任务的结束时间作为下一次任务的开始时间
后者:以上一次的开始时间作为下一次任务开始的时间
4》创建单线程执行定时任务的线程池
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
5》创建单个线程池
ExecutorService es = Executors.newSingleThreadExector();
创建单个线程池有什么用?
1)可以避免频发的创建线程和销毁线程带来的开销,
2)有任务队列可以存储多余的任务
3)当有大量的任务不能处理的时候,可以友好的执行拒绝策略
4)可以更好地管理任务
6》原始的线程池创建方式
ThreadPoolExecutor
ThreadPoolExecutor tpe =new ThreadPoolExecutor(
核心线程数(线程正常情况下),
最大线程数量(当有大量任务的时候可以创建的最多的线程数),
最大线程存活时间,
单位时间,
任务队列,
线程工厂,
拒绝策略);
核心线程数小于等于最大线程数;
当任务量小于核心线程数时,就会创建一个线程来执行此任务,
当任务量大于等于核心线程数时会先把任务放到任务队列里,当任务队列放满时,再创建线程(最大线程数),如果最大线程也使用完,就执行拒绝策略。
拒绝策略:
> 1)默认拒绝,不执行任务抛出异常:ThreadPoolExecutor.AbortPolicy();
> 2)把当前任务交给主线程执行:ThreadPoolExecutor.CallerRunsPolicy();
> 3)丢弃最老的任务:ThreadPoolExecutor.DiscardOldestPolicy();
> 4)丢弃最新的任务:ThreadPoolExecutor.DiscardPolicy();
线程池使用:
1)execute(Runnable m没有返回值)
2)submit(Runnable/Callable 有返回值,返回值使用Future接收)
线程池终止
shutdown(); //结束线程池
shutdown();//立即结束线程池
线程缺点:
1)线程的创建会开辟本地方法栈,虚拟机栈,程序计数器变成线程私有的内存,同时消耗的时候需要销毁以上三个区域,因此频繁的创建和消耗比较消耗系统资源
2)在任务量远远大于线程可以处理的任务量时,并不能有好的拒绝任务。
线程池优点:
1)可以避免频繁的创建和消耗线程。
2)可以更好地管理线程的个数和资源的个数
3)拥有更多的功能,比如线程池可以进行定时任务的执行
4)线程池可以更优化的拒接不能处理的任务
继承 Thread类:Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread 类的 start()实例方法。start()方法是一个 native 方法,它将启动一个新线程,并执行 run()方法。
实现 Runnable接口:如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个Runnable 接口。
实现 Callable接口:实现Callable接口,重写call()方法,可以返回一个 Future类型的返回值。我在上面的例子里就是用到了这种方式。
1.new初始状态
2.runnable 运行状态
3.blocked 阻塞状态
4.waiting 等待状态
5.time_waiting :超时等待状态
6.terminated:终止状态
互斥条件:该资源任意⼀个时刻只由⼀个线程占⽤。
请求与保持条件:⼀个进程因请求资源⽽阻塞时,对已获得的资源保持不放。
不剥夺条件:线程已获得的资源在末使⽤完之前不能被其他线程强⾏剥夺,只有⾃⼰使⽤完毕后才释放资源。
循环等待条件:若⼲进程之间形成⼀种头尾相接的循环等待资源关系。