欢迎关注微信公众号: 程序员小圈圈
原文首发于: www.zhangruibin.com
本文出自于: RebornChang的博客
转载请标明出处^_^
线程池的正确打开方式
线程,线程池,多线程,锁,老生常谈的知识点了,涉及很多知识,本节为笔者自己整理的知识点,方便学习记忆,分享出来仅供参考。
线程相关
线程与进程
线程是什么,进程是什么,这是很多初学者的疑问,当然老鸟大神请忽略,这里就简单的比喻下。
如果说把进程比作一条单向路,那线程就是这条路上的通道,比如高速公路单向四通道,可以看成单进程,四线程。
那么所谓的进程线程落到我们电脑硬件上是按什么划分的呢?
我们买电脑的时候,经常看到电脑参数配置里面有这样的介绍:
双核4线,八核16线之类的,这个核就是指的CPU数,几线,就是同时处理的最大线程数,说是八核十六线程的电脑,在使用起来可以达到最大并发16线程,但是,那只是使用起来,究其根本,还是执行的八核八线,只是说在处理速度跟运行效果上,可以达到使用级别的八核十六线,很简单的一个例子,电脑的任务管理器,试试看你的电脑能同时打开几个。
线程的创建和运行方式有几种
如果您是撸了两三年代码的老鸟,这里就可以忽略了。
笼统的来说,线程的创建方式分为四种。
笔者先说下自己知道的,然后具体分为几种,仁者见仁智者见智吧。
第一种,大家公认的,就是继承Thread类进行线程的创建;
第二种,也是公认的,就是实现Runnable接口;
第三种,实现Callable接口通过FutureTask包装器来创建Thread线程;
第四种,通过线程池创建线程;
//lambda写法
Executors.newFixedThreadPool(5).execute(() ->{});
线程的生命周期及线程运行状态分为几种
上面说了线程的创建方式,那这里就得赘述下现成的生命周期,和运行状态了。
首先说下,什么是线程的生命周期。
人的生命周期是出生到死亡,那线程的生命周期也是这样的,从线程的创建,到线程的销毁,就是一个完整的生命周期。说细一点,那就是:新建,就绪,运行,阻塞以及死亡。
-
新建:就是刚使用new方法,new出来的线程;
-
就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
-
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
-
阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
-
销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;
注意了,start和run哪个是就绪,哪个是执行不要记混了。
篇幅限制,这里就不说调用哪个方法进入那种状态了,也不解释锁的持有和释放了,也不说sleep和wait的用法和区别了,有兴趣的可以自行bing.
线程池及参数支持
说过线程,那不可避免的深入点,就是线程池相关的知识了。
什么是线程池
线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。由于创建和销毁线程都是消耗系统资源的,所以当你想要频繁的创建和销毁线程的时候就可以考虑使用线程池来提升系统的性能。
那线程池怎么创建呢?
//定义一个有5个线程的线程池
ExecutorService fixedTh = Executors.newFixedThreadPool(5);
ExecutorService singleTh = Executors.newSingleThreadExecutor();
ExecutorService cachedTh = Executors.newCachedThreadPool();
上面那些创建方式是最简单的,也是安全性和实用性最低的,当高并发的时候,容易引发OOM,因为那几种线程池的参数是固定的,作为一个代码人,我们要时刻 感觉,固定的,就是非最好的。
因为给定的参数并不一定适合我们自己的生产研发环境,所以,我们要自己设置参数。
线程池的常用参数及解释
参数有几个?截止到JDK1.8是7个常用参数:
-
corePoolSize:线程池中核心线程数的最大值
-
maximumPoolSize:线程池中能拥有最多线程数
-
workQueue:用于缓存任务的阻塞队列我们现在通过向线程池添加新的任务来说明着三者之间的关系。
(1)如果没有空闲的线程执行该任务且当前运行的线程数少于corePoolSize,则添加新的线程执行该任务。