技术深入分析java线程池的实现原理

上传人:876****10 文档编号:141744532 上传时间:2020-08-12 格式:DOCX 页数:22 大小:1.28MB
返回 下载 相关 举报
技术深入分析java线程池的实现原理_第1页
第1页 / 共22页
技术深入分析java线程池的实现原理_第2页
第2页 / 共22页
技术深入分析java线程池的实现原理_第3页
第3页 / 共22页
技术深入分析java线程池的实现原理_第4页
第4页 / 共22页
技术深入分析java线程池的实现原理_第5页
第5页 / 共22页
点击查看更多>>
资源描述

《技术深入分析java线程池的实现原理》由会员分享,可在线阅读,更多相关《技术深入分析java线程池的实现原理(22页珍藏版)》请在金锄头文库上搜索。

1、 技术分享|深入分析java线程池的实现原理线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池可以对线程进行统一的分配、调优和监控,并有以下好处降低资源消耗;提高响应速度;提高线程的可管理性。Java1.5引入的Executor框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行。矚慫润厲钐瘗睞枥庑赖。案例11、Executors.newFixedThreadPool(10) 初始化一个包含10个线程的线程池executor;聞創沟燴鐺險爱氇谴净。2、通过executor.e

2、xecute方法提交20个任务,每个任务打印当前的线程名;3、负责执行任务的线程的生命周期都由Executor框架进行管理。ThreadPoolExecutorExecutors是java线程池的工厂类,通过它可以快速初始化一个符合业务需求的线程池,如Executors.newFixedThreadPool方法可以生成一个拥有固定线程数的线程池。残骛楼諍锩瀨濟溆塹籟。其本质是通过不同的参数初始化一个ThreadPoolExecutor对象,具体参数描述如下:corePoolSize线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;

3、如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。酽锕极額閉镇桧猪訣锥。maximumPoolSize线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;彈贸摄尔霁毙攬砖卤庑。keepAliveTime线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize时才有用;謀荞抟箧飆鐸怼类蒋薔。unitkee

4、pAliveTime的单位workQueue用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:1、ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;2、LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;厦礴恳蹒骈時盡继價骚。3、SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;茕桢广鳓

5、鯡选块网羈泪。4、priorityBlockingQuene:具有优先级的无界阻塞队列。threadFactory创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。handler线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:鹅娅尽損鹌惨歷茏鴛賴。1、AbortPolicy:直接抛出异常,默认策略;2、CallerRunsPolicy:用调用者所在的线程来执行任务;3、DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;4、DiscardPolicy:直

6、接丢弃任务。当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。籟丛妈羥为贍偾蛏练淨。ExectorsExectors工厂类提供了线程池的初始化接口,主要有如下几种:newFixedThreadPool初始化一个指定线程数的线程池,其中corePoolSize =maximumPoolSize,使用LinkedBlockingQuene作为阻塞队列,不过当线程池没有可执行任务时,也不会释放线程。預頌圣鉉儐歲龈讶骅籴。newCachedThreadPool1、初始化一个可以缓存线程的线程池,默认缓存60s,线程池的

7、线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;渗釤呛俨匀谔鱉调硯錦。2、和newFixedThreadPool创建的线程池不同,newCachedThreadPool在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。铙誅卧泻噦圣骋贶頂廡。所以,使用该线程池时,一定要注意控制并发的任务数,否则创建大量的线程可能导致严重的性能问题。newSingleThreadExecutor初始化的线程池中只有一个线程,

8、如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行,内部使用LinkedBlockingQueue作为阻塞队列。擁締凤袜备訊顎轮烂蔷。newScheduledThreadPool初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。贓熱俣阃歲匱阊邺镓騷。实现原理除了newScheduledThreadPool的内部实现特殊一点之外,其它几个线程池都是基于ThreadPoolExecutor类实现的。坛摶乡囂忏蒌鍥铃氈淚。线程池内部状态其中AtomicInteger变量ctl的功能非常强大:利用低2

9、9位表示线程池中线程数,通过高3位表示线程池的运行状态:蜡變黲癟報伥铉锚鈰赘。1、RUNNING:-1 COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;買鲷鴯譖昙膚遙闫撷凄。2、SHUTDOWN:0 COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;綾镝鯛駕櫬鹕踪韦辚糴。3、STOP :1 COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;驅踬髏彦浃绥譎饴憂锦。4、TIDYING :2 COUNT_BITS,即高3位为010;5、TE

10、RMINATED:3 COUNT_BITS,即高3位为011。任务提交线程池框架提供了两种方式提交任务,根据不同的业务需求选择不同的方式。Executor.execute()通过Executor.execute()方法提交的任务,必须实现Runnable接口,该方式提交的任务不能获取返回值,因此无法判断任务是否执行成功。猫虿驢绘燈鮒诛髅貺庑。ExecutorService.submit()通过ExecutorService.submit()方法提交的任务,可以获取任务执行完的返回值。任务执行当向线程池中提交一个任务,线程池会如何处理该任务?execute实现具体的执行流程如下:1、worker

11、CountOf方法根据ctl的低29位,得到线程池的当前线程数,如果线程数小于corePoolSize,则执行addWorker方法创建新的线程执行任务;否则执行步骤(2);锹籁饗迳琐筆襖鸥娅薔。2、如果线程池处于RUNNING状态,且把提交的任务成功放入阻塞队列中,则执行步骤(3),否则执行步骤(4);構氽頑黉碩饨荠龈话骛。3、再次检查线程池的状态,如果线程池没有RUNNING,且成功从阻塞队列中删除任务,则执行reject方法处理任务;輒峄陽檉簖疖網儂號泶。4、执行addWorker方法创建新的线程执行任务,如果addWoker执行失败,则执行reject方法处理任务。尧侧閆繭絳闕绚勵蜆贅

12、。addWorker实现从方法execute的实现可以看出:addWorker主要负责创建新的线程并执行任务,代码实现如下:这只是addWoker方法实现的前半部分:1、判断线程池的状态,如果线程池的状态值大于或等SHUTDOWN,则不处理提交的任务,直接返回;2、通过参数core判断当前需要创建的线程是否为核心线程,如果core为true,且当前线程数小于corePoolSize,则跳出循环,开始创建新的线程,具体实现如下:识饒鎂錕缢灩筧嚌俨淒。线程池的工作线程通过Woker类实现,在ReentrantLock锁的保证下,把Woker实例插入到HashSet后,并启动Woker中的线程,其中

13、Worker类设计如下:凍鈹鋨劳臘锴痫婦胫籴。1、继承了AQS类,可以方便的实现工作线程的中止操作;2、实现了Runnable接口,可以将自身作为一个任务在工作线程中执行;3、当前提交的任务firstTask作为参数传入Worker的构造方法。从Woker类的构造方法实现可以发现:线程工厂在创建线程thread时,将Woker实例本身this作为参数传入,当执行start方法启动线程thread时,本质是执行了Worker的runWorker方法。恥諤銪灭萦欢煬鞏鹜錦。runWorker实现runWorker方法是线程池的核心:1、线程启动之后,通过unlock方法释放锁,设置AQS的stat

14、e为0,表示运行中断;2、获取第一个任务firstTask,执行任务的run方法,不过在执行任务之前,会进行加锁操作,任务执行完会释放锁;鯊腎鑰诎褳鉀沩懼統庫。3、在执行任务的前后,可以根据业务场景自定义beforeExecute和afterExecute方法;硕癘鄴颃诌攆檸攜驤蔹。4、firstTask执行完成之后,通过getTask方法从阻塞队列中获取等待的任务,如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源。阌擻輳嬪諫迁择楨秘騖。getTask实现整个getTask操作在自旋下完成:1、workQueue.take:如果阻塞队列为空,当前线程会被挂起等待;当队列

15、中有任务加入时,线程被唤醒,take方法返回任务,并执行;氬嚕躑竄贸恳彈瀘颔澩。2、workQueue.poll:如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null。釷鹆資贏車贖孙滅獅赘。所以,线程池中实现的线程可以一直执行由用户提交的任务。Future和Callable实现通过ExecutorService.submit()方法提交的任务,可以获取任务执行完的返回值。在实际业务场景中,Future和Callable基本是成对出现的,Callable负责产生结果,Future负责获取结果。怂阐譜鯪迳導嘯畫長凉。1、Callable接口类似于Runnable,只是Runnable没有返回。2、Callable任务除了返回正常结果之外,如果发生异常,该异常也会被返回,即Future可以拿到异步执行任务各种结果;谚辞調担鈧谄动禪泻類。3、Future.get方法会导致主线程阻塞,直到Callable任务执行完成。submit实现通过submit方法提交的Callable任务会被封装成了一个FutureTask对象。FutureTas

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 中学教育 > 高考

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号