线程池
简介
线程池就是先创建一些线程,他们的集合称为线程池。
使用线程池,可以很好的提高性能,线程池再系统启动时即创建大量空闲的线程,程序奖一个任务传给线程池,线程池就会启动1条线程来执行此任务,执行结束后,线程不会死亡,而是再次返回线程池成为空闲状态,等待下一任务。
工作机制
1)在线程池的编程模式下,任务是提交给整个线程池,而不是直接交给某个线程,线程池在拿到任务后就在内部寻找空闲的线程,如果有就将任务交给空闲的线程。
2)一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
使用线程池的原因
多线程运行时,系统不断的启动和关闭线程,成本非常高,会过度消耗系统资源,以及过度切换线程的危险,从而可能导致系统资源的崩溃。
常见线程池
SingleThreadExecutor
只有一条线程来执行任务,适用于有顺序的任务的应用场景
底层实现
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
程序实验:
private static void SingleThreadExecutor() { // 一池 仅有 1个线程 ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); // 模拟10个用户请求线程 try { for (int i = 1; i <= 10; i++) { singleThreadExecutor.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 处理请求"); }); } } catch (Exception e) { e.printStackTrace(); } finally { singleThreadExecutor.shutdown(); } // 程序运行结果 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 }
线程池只有1个线程,所以仅有1个线程执行任务
CachedThreadPool
可缓存的线程池,该线程池中没有核心线程,非核心线程数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时则回收线程,适用于耗时少,任务量大的情况
底层代码
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
程序实验:
private static void CachedThreadPool() { // 一池 n 个线程 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); try { for (int i = 1; i <= 10; i++) { cachedThreadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 处理请求"); }); } } catch (Exception e) { e.printStackTrace(); } finally { cachedThreadPool.shutdown(); } // 程序运行结果 // pool-1-thread-1 处理请求 // pool-1-thread-2 处理请求 // pool-1-thread-3 处理请求 // pool-1-thread-4 处理请求 // pool-1-thread-5 处理请求 // pool-1-thread-6 处理请求 // pool-1-thread-7 处理请求 // pool-1-thread-8 处理请求 // pool-1-thread-10 处理请求 // pool-1-thread-9 处理请求 }
FixedThreadPool
定长的线程池,有核心线程,核心线程的数量即为最大的线程数量,没有非核心线程。
底层代码
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
程序实验:
private static void FixedThreadPool() { // 一池5个线程 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); // 模拟10个用户请求线程 try { for (int i = 1; i <= 10; i++) { fixedThreadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 处理请求"); }); } } catch (Exception e) { e.printStackTrace(); } finally { fixedThreadPool.shutdown(); } // 程序运行结果 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-2 处理请求 // pool-1-thread-3 处理请求 // pool-1-thread-4 处理请求 // pool-1-thread-5 处理请求 }
结果分析:可以一个线程处理多个任务,但是总线程数不会改变。
ScheduledThreadPool
周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大,适用于执行周期的任务。
底层代码
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
程序实验:
private static void ScheduledThreadPool() { ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); try { for (int i = 1; i <= 10; i++) { scheduledThreadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 处理请求"); }); } } catch (Exception e) { e.printStackTrace(); } finally { scheduledThreadPool.shutdown(); } // 程序运行结果 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 // pool-1-thread-1 处理请求 }
7大核心参数
简介
出自 ThreadPoolExecutor 的一个构造方法
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; }
corePoolSize
线程池中常驻核心线程数
maximumPoolSize
线程池可容纳同时执行的最大线程数,值必须 >= 1
keepAliveTime
多余的空闲线程存活时间。当前线程池数量超过corePoolSize时,当空闲时间到达keepAliveTime值时,多余空闲线程会被销毁直至只剩下corePoolSize为止
unit
keepAliveTime的时间单位
workQueue
任务队列,被提交但尚未执行的任务
threadFactory
表示生成线程池中的工作线程的线程工厂,用于创建线程,一般为默认的线程工厂即可
handler
拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何来拒绝请求的Runable的策略
线程池工作原理
原理图
文字解释
当调用线程池的 execute() 方法时,线程池会做出以下判断:
1)如果 当前运行的线程数 小于 线程池的核心线程数,那么马上创建线程完成此任务
2)如果 运行的线程数 大于等于 线程池的核心线程数,那么将线程放进任务队列等待
3)如果 此时任务队列已满,且正在运行的线程数 小于 最大线程数,立即创建非核心线程执行这个任务
4)如果 此时任务队列已满,且正在运行的线程数 等于 最大线程数,啧线程池会启动饱和和 拒绝策略 来执行
4种拒绝策略
这4种拒绝策略都实现了 RejectedExecutionHandler
AbortPolicy(默认)
直接抛出RejectedExecutionException,异常阻止系统正常运行
CallerRunsPolicy
"调用者运行",一种调节机制,该策略既不会抛异常,又不会抛弃任务,而是将某些任务回退刀调用者,从而降低新任务的流量
DiscardOldestPolicy
抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
DiscardPolicy
直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案
阿里规范
1)
2)