什么是线程池?
线程池是基于对象池的思想,开辟一块内存空间,里面存放了众多线程,池中线程的调度交给池管理器来管理
为什么要使用线程池?
在并发任务很多的情况下,每一个线程执行很短的时间就结束了,线程频繁地创建和销毁会大大减低系统的性能,我们创建一个线程池,
当任务需要执行时,线程池会分配一条线程池中的线程去处理此任务,处理完以后放回池中继续去处理其他任务,,使线程得到复用。
java中的ThreadPoolExecutor类:
ThreadPoolExecutor构造器中各个参数的含义:
corePoolSize:核心池线程数的大小,创建线程池时,默认情况下线程池中并没有线程,直到有任务到来时才去创建线程去处理任务,除非调用
preStartAllCoreThreads()和preStartCoreThread()方法(分别创建corePoolSize个线程和一个线程),当池中的线程数达到核心池的最大线程数是,
就会把放到的任务当道缓存队列中;
maximumPoolSzie:线程池中允许的最大线程数,当线程池中的线程数大于核心池线程数并且阻塞队列已满,如果此时线程池中的最大线程数还没有达到
maximumPoolSize,那么将会新创建一个线程去处理此任务。
KeepAliveTime:线程处于空闲状态的最大存活时间,默认只有在线程池中的线程数超过corePoolSize才有用,会将线程池中处于空闲状态持续超过KeepAliveTme
的线程,直到池中的线程数不大于corePoolSize,即核心池的线程数。
unit:KeepAliveTime的时间单位
workQueue:存放任务的阻塞队列,有几种参数的选择,下面会细讲。
threadFactory:线程 工厂
handler:拒绝处理任务时的策略;
ThreadPoolExecutor有几个非常重要的方法:
executor(Runnable commad):最顶层接口Executor唯一声明的方法,调用此方法向线程提交一个任务,交给线程去执行。
shutDown():将线程池的状态置为SHUTDOWN,相当于关闭线程,将不再接受新的任务,等待阻塞队列中的任务执行完毕。
shutDownNow():将线程池的状态置为STOP,线程池将不会接受新的任务,并尝试终止当前执行的任务。
线程池几种状态:
1.RUNNING
2.SHUTDOWN
3.SHUTDOWNOW
4.TERMINALS:当线程池处于stop或shutdown状态并且所有工作线程已被销毁,阻塞已经清空,线程池状态将被置为terminals;
任务的执行过程:
1.将任务提交给线程池,即调用executor(Runnable command)方法,当任务为空时抛出空指针异常。
2.判断当前线程数是否大于核心池线程数,如果大于,会将任务尝试添加到阻塞队列中,排队等待空闲的线程取出执行,如果添加失败,
会尝试创建新的线程去执行,如果当前线程池中的线程数大于maximumPoolSize,线程池会采取任务拒绝策略(丢弃任务抛出异常等)
3.当前线程数小与核心池的线程数,那么就会调用addIfUnderCoreSize(Runnable command)这个方法,这个方法会去创建工作线程去执行当前任务,
将创建工作线程的操作放在使用Lock锁的同步块中,防处理同一任务的工作线程被多次创建,在工作线程的run()方法中有 一个while循环,当前工作线程
执行完当前任务以后,会继续到阻塞队列中取出任务执行,直到取不到任务,然后根据当前线程池的状态决定当前工作线程是否退出。
4.任务缓存队列及排队策略
workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:
1.直接提交策略:缓存队列使用synchronousQueue(同步队列)
ExcutorService cachedThreadPool=Executors.newCachedThreadPool(),默认线程池的最大线程数为Integer.MAX_VALUE,KeepAliveTime为60s,
提交任务时,任务不会进入队列,而是判断线程池中是否有一个空闲来处理该队列,如果有则入队,如果没有,则去创建一个线程来处理此任务,
适用于负载较轻的服务器。
2)无界策略使用LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE,默认KeepAliveTime为0,指定线程池最大允许的线程数
ExecutorService pool = Executors.newFixedThreadPool(最大允许线程数);
3)有界队列使用ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小,不提倡此种策略,当并发量大时,有可能造成资源耗尽。
线程池的四种拒绝策略:
1.丢弃任务并抛出异常
2.丢弃任务不抛出异常
3.丢弃队列中最老的任务
4.将任务交给调用的线程执行
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();