一、线程池
-
为什么要使用线程池:在一个应用程序中,我们需要多次使用线程,也就意味着要多次创建并销毁线程。而且创建并销毁线程的过程势必会损耗内存。
-
什么是线程池:java中出现了一种管理线程的概念,可以方便的管理线程,用一个拿一个,用完在放回到线程池中,这样可以减少内存的损耗。
-
如何创建一个线程池
-
java中已经提供了创建线程的一个类:Executor;但在创建线程池时,使用它的子类:
hreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor几个之间的关系 a、Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的; b、然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等; c、抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法; d、然后ThreadPoolExecutor继承了类AbstractExecutorService。 //ThreadPoolExecutor类中几个重要的方法 execute() submit() shutdown(); shutdownNow(); public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) ———————————————— 这是其中最重要的一个构造方法,这个方法决定了创建出来的线程池的各种属性。 corePoolSize:线程池中核心线程数量,核心线程就是无任务状态下也不可以清除。 maximumPoolSize:线程池中可以容纳的最大线程数量。 keepAliveTime:线程池中除核心线程之外的其他线程可以存活的时间。 如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0; util:计算时间的单位。7中静态属性 TimeUnit.DAYS; //天 TimeUnit.HOURS; //小时 TimeUnit.MINUTES; //分钟 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //纳秒 workQueue:等待队列,任务可以存储在任务队列中等待被执行,执行的原则就是FIFO。 等待队列类型: ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小; LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE; SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务; threadFactory:创建线程的线程工厂。 handler:是一种拒绝策略,可以在任务满了以后,拒绝某些任务。
-
-
线程池执行流程
- 首先执行判断,判断核心线程是否处于空闲状态,
- 如果不是,核心线程就先就执行任务,
- 如果核心线程已满,则判断任务队列是否有地方存放该任务,
- 若果有,就将任务保存在任务队列中,等待执行,
- 如果满了,在判断最大可容纳的线程数,
- 如果没有超出这个数量,就开创非核心线程执行任务,
- 如果超出了,就调用handler实现拒绝策略(四种执行策略)。
- 第一种ThreadPoolExecutor.AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满。
- 第二种ThreadPoolExecutor.DisCardPolicy:不执行新任务,也不抛出异常。
- 第三种ThreadPoolExecutor.DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行。
- 第四种ThreadPoolExecutor.CallerRunsPolicy:直接调用execute来执行当前任务。
-
四种常见线程池类型:
- achedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。
- SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。
- SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景。
- FixedThreadPool:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程。
-
java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:
Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池 Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池 //三个静态方法的具体实现; public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }