前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发编程(三)--线程池

并发编程(三)--线程池

作者头像
小土豆Yuki
发布2021-09-24 10:42:44
2990
发布2021-09-24 10:42:44
举报
文章被收录于专栏:洁癖是一只狗洁癖是一只狗

线程池是面试的频繁问题,今天我们先看看线程池的基本原理

代码语言:javascript
复制
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,即线程池最大线程数量,他的作用就是当核心线程和缓冲队列都慢的时候,让你提交的新任务仍然可以执行,保证会创建新的工作线程执行你提交的任务
  • keepAliveTime,存活时间,就是超过核心线程且都在空闲的线程的存活时间,即当没有可处理的任务时候,让他们别急的回收,而是先等待一会,如果等没有任务来的时候,超过时间后就可以对工作线程进行回收了
  • TimeUnit,存活时间的单位
  • workQueue,用于保存提交待执行状态的任务的阻塞队列,如有界队列ArrayBlockingQueue,链表的无界队列LinkedBlockingQueue,有且只有一个元素的同步队列SynchronousQueue,以及优先对垒PriorityBlockQueue,主要是给线程池一个缓冲空间,能够在一定程度内接受任务,而是不是直接拒绝任务
  • ThreadFactory,创建线程的工厂,使用默认的就行
  • RejectedExecutionhandler,拒绝策略,当线程达到了最大线程数,且阻塞队列也满了,所有线程都在忙,新开的线程就会触发拒绝策略

线程池基本原理图,流程如下

下面我们看看线程池的源码,前方高能请耐心看完

任务提交

任务池提交有两种方式,execute和submit,但是底层都是execute,如下源码

代码语言:javascript
复制
public void execute(Runnable command) {
       //1.先对command进行校验,如果为空就抛出NPE异常
        if (command == null)
            throw new NullPointerException();  
      //2.获取的线程上下文c,这个c存储线程状态和线程个数,一个int存储两位数,
      //  高3位表示线程状态,后29位表示线程个数
        int c = ctl.get();
      //3.判断线程池个数是否小于corePoolSize,如果是,调用addWork新建一个线程
      //   新的线程执行任务
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
      //4.如果线程池处于运行中,那么就加入阻塞队列
        if (isRunning(c) && workQueue.offer(command)) {
           //5.这里重新获取状态,因为加入队列的同时线程池的转态可能改变
            int recheck = ctl.get();
           //6.如果线程池不是运行中,就把任务从线程池删除,然后执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //7.如果线程池的线程为0,则新建一个线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //8.如果添加阻塞队列失败,说明队列满了,如果线程小于maximumPoolSize
        //  新建线程,如果大于maximumPoolSize就需要执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

worker线程获取任务执行流程

我们先看看addWorker方法

首先我们明确的是addwork就是进行新建线程,启动之后,然后执行任务,其他代码我先不要关注,只要关注我画出的框

第一处,声明构造worker线程,并把持有的thread成员变量的应用,赋值给final修饰的Thread e临时变量,

第二处,判断线程是否存活,如果存活就跑异常,因为存活的,就不需要在启动了,然后添加到工作线程集合中,是一个Hashset集合,并设置WorkAdded变量为true

第三处,就是把第一处的Thread t变量启动,也就是Worke内部持有Thread变量

其次我们看看Worker的构造函数和Worker类

代码语言:javascript
复制
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
代码语言:javascript
复制
   Worker(Runnable firstTask) {
       setState(-1); // inhibit interrupts until runWorker
       this.firstTask = firstTask; //执行的任务
       this.thread = getThreadFactory().newThread(this);//对应的线程
  }

看到这里,我就可以联想到上面的addwork方法里面work线程的启动就是启动一个线程,其中firstTask就是执行的任务,然而最终的任务也是调用run方法执行具体的业务

最后看看runWorker方法

我依然重点看我画出来的地方

第一处,就是取出worker中的task应用复制给了Runable变量

第二处,如果变量task为空,就会从队列取出任务,

第三处,然后执行任务,task.run().

最后整体的流程如下如

当我们向线程池提交任务的时候,通过调用execute执行任务(runable或者Callable实现),最终会通过Worker的构造函数传递到worker内部,这样当start启动的以后真正执行的就是worker中的Runable,也就是用户提交的Runbale

Worke线程的退出时机

对于核心线程来说,他们可以无限制的等待任务被获取执行,非核心线程则是在有限时间内获取任务,一旦worker无法获取到任务,也就是任务队列为空,玄幻就会结束,worker自己会主动去除掉在线程池中的应用,进而被回收掉并退出,

再把之前的代码拿出来看看最后几句

代码语言:javascript
复制
    public void run() {
        runWorker(this);
    }
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
           ...省略代码
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

其实在try代码里面有一个while循环结束条件就是当task==null或者队列里面为空,结束之后就会到finally执行退出代码,我在看看退出代码

上面的代码的核心流程如下图

持续关注,如果对您有一丝丝帮助,麻烦点个关注,也欢迎转发,谢谢

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-09-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档