线程池
-
概念
线程池就是首先创建一些线程,他们的集合称为线程池,使用线程池可以很好的提高性能。
-
5种状态
ThreadPoolExecutor 使用int的高3位来表示线程池状态,低29位表示线程数量
状态名 高3位 说明 Running 111(这里的第一个1是负数) 正在运行状态 ShutDown 000 不接收新的任务,但会处理阻塞队列中剩余的任务 Stop 001 不接收新的任务,并抛弃阻塞队列中的剩余任务 Tidying 010 任务全执行完毕,活动线程为0即将进入终结状态 Terminated 011 终结状态 这些信息存储在一个原子变量ctl中,目的是将线程池状态与线程个数合二为一。这样就可以用一次cas原子操作进行赋值。
-
构造方法 (7大参数)
public ThreadPoolExecutor(int corePoolSize, //核心线程数目(最多保留的线程数) int maximumPoolSize, //最大线程数目 long keepAliveTime, //生存时间-针对救急线程 TimeUnit unit, //时间单位-针对救急线程 BlockingQueue<Runnable> workQueue, //阻塞队列 ThreadFactory threadFactory, //线程工厂-可以为线程创建时起个好名字 RejectedExecutionHandler handler) //拒绝策略
-
工作方式
-
线程池中刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。
-
当线程数量达到corePoolSize(核心线程数)并没有线程空闲的时候,这时再加入任务,新加的任务会被workqueue(阻塞队列)排队,直到有空闲的线程。
-
如果队列选择了有界队列,那么任务超过了队列大小的时候,会创建 maximumPoolSize - corePoolSize数目的线程来救急。
-
如果线程达到了maximumPoolSize 仍然有新的任务,这时会执行拒绝策略。拒绝策略jdk提供了4中实现,其他著名框架也提供了实现。
记不住了。。。。。。。。先放着吧
AbortPolicy 让调用者抛出 RejectedExecutionException 异常,这是默认策略。
CallerRunsPolicy 让调用者运行任务
DiscardPolicy 放弃本次任务
DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之Dubbo 的实现,在抛出 RejectedExecutionException 异常之前会记录日志,并 dump 线程栈信息,方便定位问题
Netty 的实现,是创建一个新线程来执行任务
ActiveMQ 的实现,带超时等待(60s)尝试放入队列,类似我们之前自定义的拒绝策略
PinPoint 的实现,它使用了一个拒绝策略链,会逐一尝试策略链中每种拒绝策略 -
当高峰过去之后,超过corePollSize的救急线程如果一段时间没有任务做。需要结束节省资源,这个时间由keepAliveTime和unit来控制。
-
根据这个构造方法,JDK Executors类中提供了众多工厂方法来创建各种用途的线程池。
-
四种常见线程池
-
可缓存线程池 newCachedThreadPool
=>(先查看池中有没有以前建立的线程,如果有就直接使用,如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步性任务)
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
特点:
-
核心线程数是0,最大线程数是Integer.MAX_VALUE,救急线程的空闲生存时间是60s,意味着
=>全部都是救急线程(60s后可以回收)
=>救急线程可以无限创建
-
队列采用了SynchronizedQueue实现特点是,它没有容量,没有线程来取是放不进去的(一手交钱、一手交货)。
-
整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕。空闲1分钟后释放线程。适合任务数比较密集,但每个任务执行时间较短的情况。
-
代码测试
public class CachedThreadPool { public static void main(String[] args) { //创建一个可缓存的线程池 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { //打印正在执行的缓存线程信息 System.out.println(Thread.currentThread().getName()+"正在被执行"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } } 结果: pool-1-thread-1正在执行的线程 pool-1-thread-2正在执行的线程 pool-1-thread-2正在执行的线程 pool-1-thread-1正在执行的线程 pool-1-thread-1正在执行的线程 pool-1-thread-2正在执行的线程 pool-1-thread-2正在执行的线程 pool-1-thread-1正在执行的线程 pool-1-thread-2正在执行的线程 pool-1-thread-1正在执行的线程
-
-
newFixedThreadPool (固定大小的线程池)
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
特点:
=>核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间,
=>阻塞队列是无界的,可以放任意数量的任务
=>适用于任务量已知,相对耗时的任务
代码测试:
public class FixedThreadPool { public static void main(String[] args) { //创建一个可重用固定个数的线程池 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { fixedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"正在被执行"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } } 结果: 其实它是一组一组执行的,因为线程池大小为3,每次输出睡1秒,所以每一秒打印3个结果。 定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors() pool-1-thread-1正在被执行 pool-1-thread-2正在被执行 pool-1-thread-3正在被执行 pool-1-thread-2正在被执行 pool-1-thread-3正在被执行 pool-1-thread-1正在被执行 pool-1-thread-2正在被执行 pool-1-thread-3正在被执行 pool-1-thread-1正在被执行 pool-1-thread-2正在被执行
-
newSingleThreadPool(单例线程池)
=>他只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序FIFO,LIFO,优先级执行)
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
-
使用场景
希望多个任务排队执行。线程数固定为1,任务数多于1时,会放入无界队列排队。
任务执行完毕,这唯一的线程也不会被释放。
-
区别:
=>自己创建一个单线程串行执行任务,如果任务失败而终止那么没有任何补救措施,而线程池还会创建一个线程,保证池的正常工作
=>Executors.newSingleThreadExecutor()线程个数始终为1,不能修改
FinalizableDelegatedExecutorService应用的是装饰器模式。
只对外暴露了ExectorService接口,因为不能调用ThreadPoolExector中特有的方法。
=>Executors.newFixedThreadPool(1)初始化为1,以后还可以修改。
对外暴露的是ThreadPoolExector对象。可以强转后调用setCorePoolSize等方法进行修改
-
代码测试
public class SingleThreadPool { public static void main(String[] args) { ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int index = i; singleThreadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"正在被执行,打印的值是:"+index); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } } 结果:按顺序一个一个执行的 pool-1-thread-1正在被执行,打印的值是:0 pool-1-thread-1正在被执行,打印的值是:1 pool-1-thread-1正在被执行,打印的值是:2 pool-1-thread-1正在被执行,打印的值是:3 pool-1-thread-1正在被执行,打印的值是:4 pool-1-thread-1正在被执行,打印的值是:5 pool-1-thread-1正在被执行,打印的值是:6 pool-1-thread-1正在被执行,打印的值是:7 pool-1-thread-1正在被执行,打印的值是:8 pool-1-thread-1正在被执行,打印的值是:9
-
-
newScheduledThreadPool(定长线程池)
=>创建一个定长线程池,支持定时及周期性任务执行
代码测试
public class ScheduledThreadPool { public static void main(String[] args) { ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(6); scheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("延迟1秒后每3秒执行1次"); } },1,3, TimeUnit.SECONDS); } } 结果: 延迟1秒后每3秒执行1次 延迟1秒后每3秒执行1次 ......
-