博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程——Java线程池原理深入
阅读量:4262 次
发布时间:2019-05-26

本文共 3400 字,大约阅读时间需要 11 分钟。

      上次我们简单了解了一下什么是线程池以及Java中几种类型的线程池,今天我们来深入剖析一下线程池的原理。

1、构造

      1. 线程池管理器(ThreadPoolManager):用于创建并管理线程池

      2. 工作线程(WorkThread): 线程池中线程

      3. 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。

      4. 任务队列:用于存放没有处理的任务。提供一种缓冲机制。

注:线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务

2、线程状态

这里写图片描述

       新建状态(New):新创建了一个线程对象。

      就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
      运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
      阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
      (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
      (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
      (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
      死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

3、执行流程

这里写图片描述

      a.一个任务提交,如果线程池大小没达到corePoolSize,则每次都启动一个worker也就是一个线程来立即执行

      b.如果来不及执行,则把多余的线程放到workQueue,等待已启动的worker来循环执行
      c.如果队列workQueue都放满了还没有执行,则在maximumPoolSize下面启动新的worker来循环执行workQueue
      d.如果启动到maximumPoolSize还有任务进来,线程池已达到满负载,此时就执行任务拒绝RejectedExecutionHandler

// 流程就是:没达到corePoolSize,创建worker执行,达到corePoolSize加入workQueue//           workQueue满了且在maximumPoolSize下,创建新worker,达到maximumPoolSize,执行rejectpublic void execute(Runnable command) {    if (command == null)        throw new NullPointerException();    // 1:poolSize达到corePoolSize,执行3把任务加入workQueue    // 2:poolSize没达到,执行addIfUnderCorePoolSize()在corePoolSize内创建新worker立即执行任务    //    如果达到corePoolSize,则同上执行3    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {        // 3:workQueue满了,执行5        if (runState == RUNNING && workQueue.offer(command)) {            if (runState != RUNNING || poolSize == 0) {                // 4:如果线程池关闭,执行拒绝策略                //   如果poolSize==0,新启动一个线程执行队列内任务                ensureQueuedTaskHandled(command);            }            // 5:在maximumPoolSize内创建新worker立即执行任务            //   如果达到maximumPoolSize,执行6拒绝策略        } else if (!addIfUnderMaximumPoolSize(command))            // 6:拒绝策略            reject(command); // is shutdown or saturated    }}

4、线程销毁

      keepAliveTime:代表的就是线程空闲后多久后销毁,线程的销毁是通过worker的getTask()来实现的。

      一般来说,Worker会循环获取getTask(),如果getTask()返回null则工作线程worker终结,那我们再看看什么时候getTask()返回null

Runnable getTask() {    for (;;) {        try {            int state = runState;            if (state > SHUTDOWN)                return null;            Runnable r;            if (state == SHUTDOWN)  // Help drain queue                r = workQueue.poll();            else if (poolSize > corePoolSize || allowCoreThreadTimeOut)                // 在poolSize大于corePoolSize或允许核心线程超时时                // 阻塞超时获取有可能获取到null,此时worker线程销毁                r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);            else                r = workQueue.take();            if (r != null)                return r;            // 这里是是否运行worker线程销毁的判断            if (workerCanExit()) {                if (runState >= SHUTDOWN)                    // STOP或TERMINATED状态,终止空闲worker                    interruptIdleWorkers();                return null; // 这里返回null,代表工作线程worker销毁            }            // 其他:retry,继续循环        } catch (InterruptedException ie) {            // On interruption, re-check runState        }    }}

5、线程池关闭

      平缓关闭 shutdown:这个方法会将runState置为SHUTDOWN,会终止所有空闲的线程,同时不再接受新的任务,而仍在工作的线程不受影响,所以队列中的任务人会被执行

      立即关闭 shutdownNow:此方法将runState置为STOP,和shutdown方法的区别是,这个方法会终止所有的线程(取消所有正在执行和未执行的任务),所以队列中的任务也不会被执行了。

总结:

      通过这次学习,我们不仅知道了线程池是如何管理线程,而且还了解了线程多种状态之间的转换,这样更加便于我们对线程池的理解。之后,我们还将继续学习关于多线程的知识,让我们每天都有成长。

你可能感兴趣的文章
c++11的闭包(lambda、function、bind)
查看>>
c++的多态(重载、覆盖、隐藏)
查看>>
关于字节序
查看>>
关于vector
查看>>
基于systemV的消息队列的多进程间CS通讯实现
查看>>
linux设备模型____宏观印象
查看>>
linux设备模型_____相关函数
查看>>
linux设备模型____I2C具体实现
查看>>
linux设备模型之sysfs使用
查看>>
linux的list常用函数用法速查及应用实例
查看>>
ARM架构内核启动分析-head.S(1.1、vmlinux.lds 链接脚本分析)
查看>>
ARM架构内核启动分析-head.S(1.2、stext分析之准备阶段)
查看>>
ARM架构内核启动分析-head.S(1.3、stext分析之内存临时页表建立)
查看>>
ARM架构内核启动分析-head.S(1.4、stext分析之打开MMU并跳到start kernel)
查看>>
arm-linux之uboot向内核传递参数
查看>>
arm-linux内存页表创建
查看>>
arm-linux之bootmem分配器
查看>>
Java中数组、List、Set、Map相互转换
查看>>
java读取Excel时对单元格cell时间格式的判断
查看>>
IDEA git 忽略文件的几种方式
查看>>