线程池管理

我们都知道使用线程池能够控制线程的数量,尤其是大量的“短命”线程存在时,线程池将大大降低系统消耗(内存和CPU)。不过,线程池也同样需要管理,于是我写了本篇。
首先,我们来看看管理器的整个继承关系:



    显而易见,有ThreadPoolExecutor和ScheduledThreadPoolExecutor两个实现类,当然Executor类里也有一些内部类实现了特定的功能(如class DelegatedScheduledExecutorService),我们也可以自己通过扩展这里所有的接口、抽象类、类来实现自己的特定功能,如继承ThreadPoolExecutor类,覆写beforeExecute(),让它在每个任务开始执行前执行某些操作,还有很多可扩展功能,有兴趣的朋友可以自己摸索。
    你有两种方法创建上面管理器的实例:
1、你可以用上面介绍的两个类的那这些类的实例的构造函数来创建管理器的实例,不过你要自己配置一些诸如池最大尺寸(maximumPoolSize )的参数。
2、Executors提供各种创建上面的类的实例的方法,它默认一些参数的设置。我主要介绍
这种方法中的newFixedThreadPool(int)和newCachedThreadPool()


------------newFixedThreadPool(int)------------
     创建一个默认尺寸的池,它同时运行的线程数将是固定的,如果你要让它课同时运行的最大线程数大于初始设置的那个参数,可以调用setMaximumPoolSize()来设置额外的线程来并行处理更多的任务。
     我们调用下面的方法来添加新的任务,到底Executors是如何处理的呢?

Java代码

  • public
    void execute(Runnable command) {
  •         if (command == null)
  •             throw
    new NullPointerException();
  •         if(poolSize>=corePoolSize|| !addIfUnderCorePoolSize(command)) {
  • //如果实时连接数小于corePoolSize,那么调用addIfUnderCorePoolSize()方法
  •             if (runState == RUNNING && workQueue.offer(command)) {
  •     //如果实时连接数大于了corePoolSize,那么将任务加进等待队列中。
  •                 if (runState != RUNNING || poolSize == 0)
  •     //在执行workQueue.offer(command)的过程中shutdown了,确保所有的已经提交任务能够成功执行完。
  •                     ensureQueuedTaskHandled(command);
  •             }
  •             else
    if (!addIfUnderMaximumPoolSize(command))
  •                 reject(command); // is shutdown or saturated
  •         }
  •     }
  • 下面我们来看下poolSize>=corePoolSize为不同状态时两种执行方法:
  • private
    boolean addIfUnderCorePoolSize(Runnable firstTask) {
  •     //首先获取本类所有同步方法的锁
  •         Thread t = null;
  •         final ReentrantLock mainLock = this.mainLock;
  •         mainLock.lock();
  •         try {
  •             if (poolSize < corePoolSize && runState == RUNNING)
  •                 t = addThread(firstTask);
  •         } finally {
  •             mainLock.unlock();
  •         }
  •         if (t == null)
  •             return
    false;
  •         t.start();
  •         return
    true;
  •     }
  • private
    boolean addIfUnderMaximumPoolSize(Runnable firstTask) {
  •     //首先获取本类所有同步方法的锁
  •      Thread t = null;
  •         final ReentrantLock mainLock = this.mainLock;
  •         mainLock.lock();
  •         try {
  •             if (poolSize < maximumPoolSize && runState == RUNNING)
  •                 t = addThread(firstTask);
  •         } finally {
  •             mainLock.unlock();
  •         }
  •         if (t == null)
  •             return
    false;
  •         t.start();
  •         return
    true;
  •     }
  • 几乎完全一样,估计author Doug Lea当初也是直接copy的吧。
  • 这两个方法都调用了
  • private Thread addThread(Runnable firstTask) {
  •         Worker w = new Worker(firstTask);
  •     //这里并没有区分maximumPoolSize 和corePoolSize
  •     Thread t = threadFactory.newThread(w);
  •         if (t != null) {
  •             w.thread = t;
  •             workers.add(w);//workers并没有尺寸的限制
  •             int nt = ++poolSize;
  •     //这一步维护一个管理器使用过程中的最大尺寸,没什么好说的。
  •             if (nt > largestPoolSize)
  •                 largestPoolSize = nt;
  •         }
  •         return t;
  •     }
  • 于是我认为发现管理器在对待aximumPoolSize 和corePoolSize 时根本没有什么区别,可是这是不正确的,至于为什么,大家可以自己去探索!

public void execute(Runnable command) {        if (command == null)            throw new NullPointerException();        if(poolSize>=corePoolSize|| !addIfUnderCorePoolSize(command)) {//如果实时连接数小于corePoolSize,那么调用addIfUnderCorePoolSize()方法            if (runState == RUNNING && workQueue.offer(command)) {        //如果实时连接数大于了corePoolSize,那么将任务加进等待队列中。                if (runState != RUNNING || poolSize == 0)        //在执行workQueue.offer(command)的过程中shutdown了,确保所有的已经提交任务能够成功执行完。                    ensureQueuedTaskHandled(command);            }            else if (!addIfUnderMaximumPoolSize(command))                        reject(command); // is shutdown or saturated        }    }下面我们来看下poolSize>=corePoolSize为不同状态时两种执行方法: private boolean addIfUnderCorePoolSize(Runnable firstTask) {        //首先获取本类所有同步方法的锁        Thread t = null;        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            if (poolSize < corePoolSize && runState == RUNNING)                t = addThread(firstTask);        } finally {            mainLock.unlock();        }        if (t == null)            return false;        t.start();        return true;    }  private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {        //首先获取本类所有同步方法的锁         Thread t = null;        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            if (poolSize < maximumPoolSize && runState == RUNNING)                t = addThread(firstTask);        } finally {            mainLock.unlock();        }        if (t == null)            return false;        t.start();        return true;        }几乎完全一样,估计author Doug Lea当初也是直接copy的吧。这两个方法都调用了private Thread addThread(Runnable firstTask) {        Worker w = new Worker(firstTask);        //这里并没有区分maximumPoolSize 和corePoolSize         Thread t = threadFactory.newThread(w);        if (t != null) {            w.thread = t;            workers.add(w);//workers并没有尺寸的限制            int nt = ++poolSize;        //这一步维护一个管理器使用过程中的最大尺寸,没什么好说的。            if (nt > largestPoolSize)                largestPoolSize = nt;        }        return t;    }于是我认为发现管理器在对待aximumPoolSize 和corePoolSize 时根本没有什么区别,可是这是不正确的,至于为什么,大家可以自己去探索!
      ThreadPoolExecutor类内部有一个:
private final HashSet<Worker> workers = new HashSet<Worker>();
其中Worker类是ThreadPoolExecutor一个内部类,实现了Runable接口。在addIfUnderMaximumPoolSize()和addIfUnderCorePoolSize()两个方法中将任务添加进这个workers[]中,这个数组维护一个正在运行的任务组,这个数组中的一个元素对应一个正在运行的线程,如果一个线程以外死亡,数组中的元素没有被移除,管理器将自动创建一个新的线程继续从头开始执行刚刚那个以外死亡的数组对应的任务。
       如此神奇?那是如何实现的?
       很简单,ThreadPoolExecutor维护的线程的run方法都是在这个loop中的,

Java代码

  • while (task != null || (task = getTask()) != null) {
  •                     runTask(task);
  •                     task = null;
  •                 }

while (task != null || (task = getTask()) != null) {                    runTask(task);                    task = null;                }
如果意外死亡,task=null不执行,重新判断条件的时候再次调用runTask(task);即,死亡的是runTask(task)方法内部的run()调用而已。

      说到这里,大家应该明白了,管理器无非就是用BlockingQueue<Runnable> workQueue队列(注意这个队列是线程安全的,挺有意思)来缓冲多出来的任务,而总是有不大于maximumPoolSize(注意,这里不是corePoolSize )的线程在运行着,再有点异常死亡处理的能力而已。



--------newCachedThreadPool()--------

Java代码

  • 这个方法源码:
  • public
    static ExecutorService newCachedThreadPool() {
  •         return
    new ThreadPoolExecutor(0, Integer.MAX_VALUE,
  •                                       60L, TimeUnit.SECONDS,
  •                                       new SynchronousQueue<Runnable>());
  •     }

这个方法源码: public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue<Runnable>());        }

    原来,就是让corePoolSize =0,maximumPoolSize=最大整数,然后设置空闲线程的存活时间为60s而已。看到这里,大家或许会冒出这样一个疑问:既然corePoolSize 是0,那么不是运行不了任何线程吗?呵呵,大家如果认真看了前面的文章就会有此疑问了。看同时刻运行的线程最大数是看参数maximumPoolSize不是corePoolSize 。
至于存活时间设置,那是很有必要,否则

Java代码

  • while (task != null || (task = getTask()) != null) {
  •                     runTask(task);
  •                     task = null;
  •                 }

while (task != null || (task = getTask()) != null) {                    runTask(task);                    task = null;                }
getTask方法中从待执行任务缓冲队列中poll()任务的时候会有一个存活时间的超时机制,如果超时将返回null,这个线程将因为一系列连锁反应,最终死亡。

      好了,看似简单的Executor我砍了这么多,顺序整理的不是很好,大家将就看看吧。
总结一下,在设计这几个类的时候用到集合、同步(锁和阻塞队列)、枚举(TimeUnit)、多线程、安全控制(本文没有涉及)、工厂设计模式等等知识点,不简单哪^-^

 

本文转自www.35java.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中,线程池是一种用于管理和复用线程的机制,可以提高系统的性能和资源利用率。通过使用线程池,可以避免频繁创建和销毁线程的开销,提高线程的复用性和执行效率。 Java提供了ThreadPoolExecutor类作为线程池的实现类,它继承自AbstractExecutorService类,是Java并发包(java.util.concurrent)中线程池的核心实现类。 要使用线程池,首先需要创建一个ThreadPoolExecutor对象,并指定线程池的参数,例如核心线程数、最大线程数、线程空闲时间等。然后,可以将任务提交给线程池执行,线程池会自动管理线程的创建、复用和销毁。 以下是一个简单的Java线程池管理的示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // 创建一个固定大小的线程池,核心线程数为5 ExecutorService executor = Executors.newFixedThreadPool(5); // 提交任务给线程池执行 for (int i = 0; i < 10; i++) { final int taskId = i; executor.execute(new Runnable() { public void run() { System.out.println("Task " + taskId + " is being executed."); } }); } // 关闭线程池 executor.shutdown(); } } ``` 上述代码中,通过Executors类的静态方法newFixedThreadPool创建了一个固定大小为5的线程池。然后,通过execute方法提交了10个任务给线程池执行。每个任务都会打印出自己的任务ID。最后,调用shutdown方法关闭线程池

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值