信息化 频道

面向多核处理器实现高性能中间件

    3.3使用线程池—Executor

    大多数并发应用程序是以执行任务(task)为基本单位进行管理的。

    “为每个任务创建一个线程,当任务完成时撤消对应的线程”是一种常见的线程生命周期管理策略,但这种方法存在明显的缺陷:①线程的创建需要一定的时间,给任务请求的响应带来延迟,线程的创建和撤消也给操作系统带来额外的管理负担,若这种“创建和撤消”频繁,则将明显增加系统的额外开销。②如果创建的线程数量多于系统中的处理器数,则这些线程不但占用了更多内存,而且加剧了线程对处理器的竞争,同时也增加了垃圾回收站的压力。

    一种更加合理的使用多线程的方法是使用线程池(Thread Pool)。 JSR-166提供了一个灵活的线程池实现:Executor 框架。这个框架可以用于异步任务执行,而且支持很多不同类型的任务执行策略。它还为任务提交和任务执行之间的解耦提供了标准的方法,为使用 Runnable 描述任务提供了通用的方式。 Executor 的实现还提供了对生命周期的支持和 hook 函数,可以添加如统计收集、应用程序管理机制和监视器等扩展。

    在线程池中执行任务线程,可以重用已存在的线程,免除创建新的线程。这样可以在处理多个任务时减少线程创建、消亡的开销。同时,在任务到达时,工作线程通常已经存在,用于创建线程的等待时间不会延迟任务的执行,因此提高了响应性。通过适当的调整线程池的大小,在得到足够多的线程以保持处理器忙碌的同时,还可以防止过多的线程相互竞争资源,导致应用程序在线程管理上耗费过多的资源。

    Executor 默认提供了一些有用的预设线程池,可以通过调用 Executors 的静态工厂方法来创建。

    newFixedThreadPool:提供一个具有最大线程个数限制的线程池。

     newCachedThreadPool:提供一个没有最大线程个数限制的线程池。

    newSingleThreadExecutor:提供一个单线程的线程池。保证任务按照任务队列说规定的顺序(FIFO,LIFO,优先级)执行。

    newScheduledThreadPool:提供一个具有最大线程个数限制线程池,并支持定时以及周期性的任务执行。

    下面的代码演示了线程池的使用:

    Executor executor = Executors.newFixedThreadPool(10);

    Runnable task = new Runnable() {

    public void run() {

    System.out.println("task over");

    }

    };

    executor.execute(task);

    其实上述这些工厂方法返回的Executor 都是ThreadPoolExecutor()类的常用实例,能满足大部分线程池的应用需求。ThreadPoolExecutor()类的构造方法,还能用于更专门的线程池定制:

    public ThreadPoolExecutor(

    int corePoolSize,

    int maximumPoolSize,

    long keepAliveTime,

    TimeUnit unit,

    BlockingQueue workQueue,

    RejectedExecutionHandler handler

    )

    其中:

    corePoolSize:核心池的大小,即池中(即使没有任务执行)必须保持的线程数。

    maximumPoolSize:池中允许的最大线程数。当工作队列(workQueue)充满后添加新线程时所限制线程数的上界。

    keepAliveTime:当池中的线程数大于核心池尺寸时,空闲线程被回收前等待新任务的最长存活时间。

    unit:keepAliveTime 参数的时间单位。

    workQueue:任务队列。保持着由execute 方法提交的、因池中暂时没有空闲线程为之服务而阻塞的Runnable 任务。

    handler:异常处理程序。

    下面这段代码就定制了一个线程池:线程数最少为5,最多为10,多余线程做多存活时间1分钟,队列任务数100。

    Executor pool =new ThreadPoolExecutor( 5, 10, 60, TimeUnit.SECONDS,

    new ArrayBlockingQueue(100) );
 

0
相关文章