• 关于线程池的几个问题


    这里以java中的   ThreadPoolExecutor  线程池来说明:

    1:线程池的几个重要的参数,这几个参数有什么作用?

        在线程中有 核心池大小,最大池大小,任务队列  这几个比较重要的参数。

        以下是几个重要参数在源码中的体现

    //  核心池大小
    private volatile int corePoolSize;
    //  最大池大小
    private volatile int maximumPoolSize;
    //  任务队列
    private final BlockingQueue<Runnable> workQueue;

       作用:

       corePoolSize:当需要执行的任务数量小于corePoolSize则新创建一个线程,并且执行任务,如果执行任务数大于corePoolSize则将任务,则将需要执行的任务放入到workQueue队列中,以下是代码:

    //  小于核心池大小,则新增一个线程然后执行
    if (workerCountOf(c) < corePoolSize) {
                if (addWorker(command, true))
                    return;
                c = ctl.get();
            }
    //  当任务数大于等于核心池大小,则放到队列中
    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);
            }

       maximumPoolSize:当队列中的任务已经添加满了,再往池中添加任务,如果 池中线程数<maximumPoolSize则会新建线程,反之则废弃该任务

    // 添加到队列中失败,则会新建线程去执行任务
    else if (!addWorker(command, false))
                reject(command);

      workQueue:当池中的线程数大于等于核心池的大小,则将需要执行的任务放入到队列workQueue中。

    2:线程池的工作流程是什么?

      分为3中场景的流程:1:池中线程数<核心池数   2:池中线程数大于等于核心池数但小于最大池数 3:池中线程=最大池数

      场景1:新建一个线程并且执行任务,进入到 addWorker 的方法中:

    // 添加一个任务到workSet中,core  为true
    private boolean addWorker(Runnable firstTask, boolean core) {
            retry:
            for (;;) {
                int c = ctl.get();
                // 获取线程池的运行状态
                int rs = runStateOf(c);
                ……
                for (;;) {
                    // 线程池中运行的线程数
                    int wc = workerCountOf(c);
                    
                    if (wc >= CAPACITY ||
                        wc >= (core ? corePoolSize : maximumPoolSize))
                        return false;
                    //  原子增加 执行线程的数量 
                    if (compareAndIncrementWorkerCount(c))
                        // 跳出循环 进入循环体外的逻辑
                        break retry;
                     ……
                }
            }
    
            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 {
                        ……
                        int rs = runStateOf(ctl.get());
    
                        if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                            if (t.isAlive()) // precheck that t is startable
                                throw new IllegalThreadStateException();
                            //  将任务放到 workers 集合中 在锁中添加
                            workers.add(w);
                            int s = workers.size();
                            if (s > largestPoolSize)
                                largestPoolSize = s;
                            workerAdded = true;
                        }
                    } finally {
                        mainLock.unlock();
                    }
                    if (workerAdded) {
                        // 添加成功后执行任务
                        t.start();
                        workerStarted = true;
                    }
                }
            } finally {
                if (! workerStarted)
                    addWorkerFailed(w);
            }
            return workerStarted;
        }

     下面看看具体的执行方法  即   t.start(); 方法的具体逻辑,代码如下:

    //执行 start方法  会运行run 方法
    public void run() {
                runWorker(this);
            }

    下面是  runWorker  的方法:task != null表示需要执行的任务不为空,在池中线程<核心池大小为true     getTask():从队列中获取任务并且执行。

    final void runWorker(Worker w) {
            Thread wt = Thread.currentThread();
            Runnable task = w.firstTask;
            w.firstTask = null;
           try {
                // 进入while循环,使该线程可以一直处理任务,线程不会执行完一次任务就退出,在线程池退出前,线程一直工作,以免线程资源的浪费
                while (task != null || (task = getTask()) != null) {
                    w.lock();
                    
                    try {
                        beforeExecute(wt, task);
                        Throwable thrown = null;
                        try {
                            // 具体执行任务
                            task.run();
                        } 
                    } finally {
                        task = null;
                        w.completedTasks++;
                        w.unlock();
                    }
                }
                completedAbruptly = false;
            } finally {
                processWorkerExit(w, completedAbruptly);
            }
        }

    当这个线程执行完任务之后,并不会销毁线程,而是不断遍历队列中的任务,如果队列中有任务,则执行相应的任务。这个是线程池的主要实现逻辑,即达到了线程复用的目的。

     场景2:池中线程数大于等于核心池数但小于最大池数  

    //  这里将任务放入到队列中
    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);
            }

    放入到队列中,具体的执行时刻是在,场景1中的  while循环中的  getTask() 获取到任务,然后执行

    场景3:

    // 将任务加到workerSet中,并且新建一个线程去执行,这里的core标识为false, 其余逻辑和场景1中是一致的
    else if (!addWorker(command, false))
                reject(command);

    3:如何设置核心池的大小?如何计算?

    如果是计算密集型,这时是不需要切换线程来计算的,开启的线程数为  CPU个数

    如果是IO阻塞密集,则根据实际的阻塞时间来进行计算,如:执行计算时间  为 a   阻塞时间为  b  这时线程池的大小应该设置为  (b/a+1)*cpu 核数   这个只是理论指导值    

  • 相关阅读:
    PCB Genesis加邮票孔(线与线)实现算法
    PCB 无需解压,直接读取Zip压缩包指定文件 实现方法
    PCB MS SQL CLR聚合函数(函数作用,调用顺序,调用次数) CLR说明
    PCB MS SQL表值函数与CLR 表值函数 (例:字符串分割转表)
    PCB MS CLR 聚合函数 joinString加排序实现
    PCB 奥宝LDI 输出自动改周期检测内容
    如何介绍项目
    二叉树的深度
    51单片机汇编指令手册
    SSM父子工程搭建
  • 原文地址:https://www.cnblogs.com/beppezhang/p/14052976.html
Copyright © 2020-2023  润新知