Java线程池--------ThreadPoolExecutor

Java线程池

导语:
在Java中,大家都知道,多线程开发往往都面试中以及工作中经常处理的一类技术难题,而线程池则是我们在开发中,必不可少的并发框架,因为它可以有效的利用线程,使其发挥最大的作用。想要做到这一点,我们应该对于它的原理以及是如何进行工作的,掌握清楚。

当我们在线程池中新提交一个任务时,线程池会进行如下的操作:
(1)检查线程池中的corePoolSize:如果当前运行线程小于corePoolSize,就进行线程创建来执行任务(需要获得全局锁)
(2)当核心线程池中正在运行的线程大于或者等于corePoolSize时,则将该任务放入任务队列BlockingQueue中。
(3)如果任务队列已满,则在线程池中创建线程进行任务执行。
(4)当创建线程之后,会导致线程池中的线程数大于maximumPoolSize的话,则会将任务交给饱和策略来进行处理。
这张图大家可以加深理解
下面我么能通过JDK - 8源码看看,execute()的执行过程:

 public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         * 
         * 如果运行的线程少于corePoolSize线程,请尝试使用给定命令作为其第一个任务来启动新线程。
         * 对addWorker的调用从原子上检查runState和workerCount,从而通过返回false来防止在不应该添加线程的情况下发出虚假警报。
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 如果一个任务可以成功排队,那么我们仍然需要仔细检查是否应该添加一个线程(因为现有线程自上次检查后就死掉了)或该池自进入此方法后就关闭了。
         * 因此,我们重新检查状态,并在必要时回滚排队(如果已停止),或者在没有线程的情况下启动新线程。
         * 
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         * 
         * 如果我们无法将任务排队,则尝试添加一个新线程。如果失败,则表明我们已关闭或已饱和,因此拒绝该任务
         */
        
        // 原子类实例变量 ctl 是一个主池控制常量,用于进行workCount和runState字段的更改
        // workCount 表示线程数
        // runState 表示线程的生命周期,包括运行,关闭等
        int c = ctl.get();
        // 检查核心线程池中线程的数量是否小于corePoolSize
        if (workerCountOf(c) < corePoolSize) {
            // 当满足上面的条件时,我们通过调用addWorker方法,来进行任务的线程分配
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 若corePoolSize小于workerCount,
        // 则说明,核心线程池中没有空闲的工作线程
        // 检查当前线程是否存活,并且能否添加到workQueue中
        if (isRunning(c) && workQueue.offer(command)) {
            // 我们仍然需要仔细检查是否应该添加一个线程(因为现有线程自上次检查后就死掉了),或者自从进入此方法以来该池已关闭。
            int recheck = ctl.get();
            // 因此,我们重新检查状态,并在必要时回滚排队(如果已停止),或者在没有线程的情况下启动新线程。
            if (! isRunning(recheck) && remove(command))
                // 这里也就是将该任务交给了我们的饱和策略进行处理
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 若不能添加到工作队列中去,我们再次尝试加入到线程池中去。
        // 如果加入失败,则提交给饱和策略进行处理
        else if (!addWorker(command, false))
            reject(command);
    }

这里简单提一下工作线程的概念,就是说,在执行execute()方法时,通过调用
private boolean addWorker(Runnable firstTask, boolean core)来将我们的任务封装为一个工作线程,使其能在线程池中工作。如果该任务被放到了BlockingQueue中时,线程会不断的从中获取任务来执行。

那我们再来看看线程池的一些基本属性和线程池的一些创建方法

1、maximumPoolSize:最大池大小。请注意,实际的最大值在内部受CAPACITY限制。
而这里的CAPACITY我们简单介绍一下:

// 为了将它们打包为一个int,我们将workerCount限制为(2 ^ 29)-1(约5亿个)线程,而不是(2 ^ 31)-1(20亿个)可表示的线程。
//如果将来有问题,可以将该变量更改为AtomicLong,并调整以下移位掩码常量。
//但是在需要之前,使用int可以使此代码更快,更简单。
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

2、corePoolSize:核心线程数的大小,通过源码中的注释,我么你可以获悉,这个参数是动态的,它是保持活动状态线程的最低数量,需要注意的一点是,即使线程池中有空闲线程,当工作线程数量没有达到此参数时,依旧会创建新的线程,而不是空闲的线程

3、workQueue:用于保存等待执行任务的阻塞队列,常见的队列有下面几种:
1)ArrayBlockingQueue:FIFO,基于有界数组实现的阻塞队列
2)LinkedBlockingQueue:FIFO,基于链表实现的阻塞队列
3)SynchronousQueue:不存储任务的阻塞队列,插入操作必须等到其他线程执行完移除操作,否则以一直会处于阻塞状态。
4)PriorityBlockingQueue:基于优先级的无限阻塞队列。

吞吐量: SynchronousQueue > LinkedBlockingQueue > ArrayBlockingQueue

4、饱和策略:是当线程池和任务对流都以达到峰值,来进行这种饱和状态下的一些策略模式,具体有下面几种:
1)AbortPolicy:直接抛出异常
2)CallerRunsPolicy:只用调用者所在线程来运行线程
3)DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
4)DiscradPolicy:不进行处理,直接丢弃掉

execute()与submit()方法

execute():方法调用没有返回值
submit():方法调用有返回值,而我们截至目前,一直使用Future接口来接收处理结果,这一点到下一篇文章中讲解。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值