线程池与ThreadPoolExecutor学习笔记

       学习和使用Java的时间也不算短了,一直想找个机会好好学习线程池相关的技术细节,最近正好比较闲,因此抽空来把JDK 1.7线程池的实现给研究了一下。
       线程池技术,不管是对于服务器端开发还是客户端开发都很重要。线程池最大的好处在于减少对象的创建和销毁带来的资源消耗。在面向对象程序编程中,创建和销毁对象是很消耗资源的。对于Java来说,更是如此,Java虚拟机会跟踪每一个对象,以便在对象被销毁后进行垃圾回收,这就是一些“池化技术”产生的原因。服务器端开发我不太熟悉,对于客户端开发来说。经常需要在非UI线程处理一些工作,如果每次都创建一个线程的话,对于CPU、内存、电池都受限制的手机来说,这显然是不可接受的。因此我们在进行Android开发时也采用线程池的技术来避免资源的消耗。由于我是Android程序猿,使用的语言是Java,因此我就使用JDK的线程池实现来讲解。
       想要学习了解一个类,首先要从它的构造函数开始学习。首先我们来看一下它的一个常见的构造函数。

/**
* @param corePoolSize 线程池中维护的目标线程个数,在运行一段时间以后线程个数与corePoolSize相等
* @param maximumPoolSize 线程池中中维护的最大线程个数
* @param keepAliveTime 当线程池中的线程个数大于corePoolSize,并且由有处于idle状态的线程时,最大的等待下一个任务的时间
* @param unit keepAliveTime的时间单元
* @param workQueue 保存要运行的Runnable对象的的队列
* @param threadFactory 当executor需要创建新的线程时使用的factory
*/
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

       由构造函数可知,ThreadPoolExecutor可以创建一个线程池不断处理提交过来的Runnable对象,线程池在运行一段时间以后线程的个数等于corePoolSize,maximumPoolSize是线程池中最大的线程个数,当线程池中的线程个数大于corePoolSize并且由处于idle状态的线程,经过keepAliveTime之后线程会被销毁。线程池使用BlockingQueue来保存提交执行Runnable,当queue为空时,线程持续等待并被阻塞住。
       接下来说一下ThreadPoolExecutor这个类中贯穿始终的一些变量和方法,这些正是ThreadPoolExecutor类设计的巧妙之处。ctl是一个atomic integer,它负责保存线程池的运行状态以及当前线程池的worker线程个数。整个线程池的最大容量是2^29 - 1,也就说ctl的低29位用来存储worker线程的个数,ctl的高三位,用来存储线程池的状态,如RUNNING、SHUTDOWN、STOP等等。源码中关于这么设计的解释是2^29 - 1也已经是一个非常大的数了,以后再扩展的话再换成atomic long也很方便,而且由于在ThreadPoolExecutor中需要频繁查询线程池状态以及worker线程个数,而移位运算效率又是相对比较高的,因此采用这样的方式来设计。废话少说,下面直接上源码。

    //负责保存线程池状态以及worker线程数量
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //worker线程数量最大值,2^29 - 1
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    //线程池的各种运行状态
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    //取出ctl的高3位,获得线程池状态
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    //取出ctl的低29位,获得worker线程个数
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

       接下来是ThreadPoolExecutor的一些重要方法。

public void execute(Runnable command) {
        //首先判空,空则抛异常
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        //如果worker线程个数小于corePoolSize
        if (workerCountOf(c) < corePoolSize) {
            //调用addWorker方法,此方法稍后解释
            //大致就是新增一个线程,并把command作为此线程的第一个任务
            //如果新增并且执行成功,直接返回
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //如果worker线程个数 >= corePoolSize
        //判断线程池运行状态,并且将command添加到保存任务的Queue中
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //再次检查线程池状态,如果挂了,则从Queue中移除command,并且关闭线程池
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //否则线程池正常运行的话,如果worker线程的个数等于0,那么则新增线程
            //这也就是我工作的项目中使用的线程池corePoolSize和maximumPoolSize相同的原因
            //因为线程个数到达corePoolSize之后新增线程的条件很严苛,绝大多数情况下maximumPoolSize是无用的
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //当线程池的状态不是running
        //或者虽然处于running,但是队列已经满了,拒绝添加新任务
        else if (!addWorker(command, false))
            reject(command);
    }

       对于execute方法的总结,(1)当线程池接收一个新的任务时,如果当前线程个数小于corePoolSize,那么直接调用addWorker方法新增线程,并且将command作为第一个任务进行执行。(2)如果当前线程个数大于等于corePoolSize,首先判断线程池运行状态,如果状态正常,然后试图将任务添加到队列中。如果线程池状态异常或者缓存队列已经满了,尝试新增worker线程,如果新增worker线程仍失败,则拒绝command。(3)线程个数大于等于corePoolSize,线程池状态正常,并且将任务成功添加到缓存队列中,那么再次检查线程池状态。如果线程池挂了,那么拒绝任务。否则再次检查状态仍正常的话,如果worker线程个数为0,那么新增worker线程。

    /**
    * @param firstTask 新增线程执行的第一个runnable对象
    * @paran core 是否以corePoolSize为线程个数限制
    */
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            //检查线程池状态
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                //状态异常则直接返回
                return false;

            for (;;) {
            //检查worker线程数量
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    //状态异常则直接返回
                    return false;
                //增加worker线程数量
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                //状态异常则重试
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //创建新的worker对象
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //往worker线程set添加对象
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //如果添加worker成功,执行runnable
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

       addWorker方法总结:此方法主要是完成添加worker对象的工作,添加成功之后则执行 runnable,不过 runnable有可能为空。

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //此方法的核心在于此,worker在addWorker 被创建之后,run方法直接被调用,而run方法又会调用至此方法。在此方法中,通过while 循环,不断调用getTask从blockingQueue中取出任务来执行,如果缓存队列为空,则线程被阻塞,直到有新的任务被放入缓存队列中
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

       runWorker()的方法的核心在于通过while 循环,不断调用getTask从blockingQueue中取出任务来执行,如果缓存队列为空,则线程被阻塞,直到有新的任务被放入缓存队列中。
       至此,线程池的简单学习笔记已经到此结束了,线程池对于移动端开发来说有着非常重要的意义,必须要掌握好!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值