Java并发编程(二)--线程池

来源:互联网 发布:哈登15 16赛季数据 编辑:程序博客网 时间:2024/03/29 22:53

Java并发编程(一)–Executor,Callable,Future和FutureTask

上一篇文章记录了Executor,Callable,Future和FutureTask的简单介绍,这一篇主要记录一下多线程中的线程池的用法。

通过学习上一篇文章中的Executor,现在我们知道,可以通过Executor来把任务的提交和执行解耦开来,提交上去的任务,提交者不用关心任务是怎么执行的。想要实现提交上去的任务异步执行,最简单的方法就是上篇文章中例子中写的那样,每次new一个Thread去执行。但显然这样做效率很低,无限制的创建线程,也会占用很多资源。这时候我们就可以通过线程池来管理我们的线程们。

ExecutorService

ExecutorService其实是Executor的一个拓展,它是继承自Executor的。Executor接口只有一个execute()方法,参数是一个Runnable,这就决定了它是无法返回执行结果的。而ExecutorService又增加了几个提交方式,名字是submit,共有三个重载,上一篇文章最后的例子中,我们其实就用到了ExecutorService。
除此之外,ExecutorService还增加了一些其他的接口,可以去看一下源码,注释写的也都很清楚,这里就不贴上来了。

ThreadPoolExecutor

这个就是我们今天的主角了,看名字也可以猜到,它就是线程池本人。它是ExecutorService的一个实现类,通过某种策略来管理其中的线程。通过使用线程池我们可以避免创建大量线程带来的开销和资源占用,并能给我们一定管理这些线程的能力。

ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory,                              RejectedExecutionHandler handler)

这个是它的参数列表,我们来看一下每个参数代表的含义。

  1. corePoolSize
    线程池中保留的线程数量,即使线程是空闲的。

  2. maximumPoolSize
    线程池中允许存在的最大线程数

  3. keepAliveTime
    当线程池中的线程数超过了corePoolSize时,允许额外的空闲线程存在的时间。

  4. unit
    keepAliveTime的时间单位

  5. workQueue
    保存处于等待状态任务的队列。这个队列只会保存由execute方法提交的Runnable任务。
    这个阻塞队列是一个BlockingQueue,关于BlockingQueue,可以看一下这篇大神的文章
    http://wsmajunfeng.iteye.com/blog/1629354

  6. threadFactory
    用来创建新线程的工厂

  7. handler
    当线程数超过maximumPoolSize,而且workQueue也已经满了的时候,采用的处理策略。
    这是一个RejectedExecutionHandler,它共有四种实现
    1.AbortPolicy
    默认的策略,ThreadPoolExecutor里面有个defaultHandler,就是一个AbortPolicy。当我们选用的构造没有handler参数时,就会默认使用它。表示直接抛出一个RejectedExecutionException。
    2.CallerRunsPolicy
    直接在调用的线程中执行任务。如果executor已经shut down,就丢弃该任务。
    3.DiscardOldestPolicy
    丢掉最老的仍未执行的任务,然后再尝试执行该任务。如果executor已经shut down,就丢弃该任务。
    4.DiscardPolicy
    直接丢弃该任务。
    除了这四种之外,我们也可以自己实现RejectedExecutionHandler来定义需要的策略。

参数说完了,接下来说一下ThreadPoolExecutor具体的原理。
开始时线程池中是空的(除非我们调用prestartCoreThread或prestartAllCoreThreads来启动一个或所有核心线程),每当我们提交一个任务,就会启动一个线程来执行它,直到线程数到达了我们设置的corePoolSize,这时候就会把任务放进阻塞队列workQueue中。再继续提交到workQueue也满了的时候,就会创建线程去执行任务,直到线程总数到达maximumPoolSize。这时候如果再提交就会按照handler策略来处理任务。线程执行任务完毕后,又没有任务来,就会在keepAliveTime时间后被结束掉。corePoolSize中的线程默认是不会结束的,除非我们allowsCoreThreadTimeOut来允许coreThread超时。

注意:如果想要使用LinkedBlockingQueue,一定要注意有没有给他设置size。不设size的LinkedBlockingQueue是一个无界的队列,这种情况下,它是不会满的,也就是说我们设置的maximumPoolSize其实是无意义的,如果一直提交任务,线程池中的任务又没有执行完,线程数会无限增加,资源会被消耗殆尽。

Executors

这是一个创建线程池的工厂类,提供了一系列的静态工厂方法来创建一些常用的线程池。

  • newFixedThreadPool
    创建一个固定线程数的线程池,是使用LinkedBlockingQueue创建的。每提交一个任务创建一个新线程,直到到达设置的size。当其中一个线程在执行任务中遇到了异常退出,会新建一个线程补上。

  • newSingleThreadExecutor
    创建一个只包含一个线程的线程池,是使用LinkedBlockingQueue创建的。当线程在执行任务中遇到了异常退出,会新建一个线程补上。它和newFixedThreadPool(1)的区别是,newSingleThreadExecutor之后无法通过setCorePoolSize方法来重新配置核心线程数。

  • newCachedThreadPool
    创建一个不定大小的线程池,是使用SynchronousQueue创建的。提交任务时会重用之前的线程,如果没有能重用的线程,就创建一个新的。空余的线程会在60秒后被回收。这个线程池在一些有很多短生命周期的异步线程的程序中可以提高效率。但这个线程池的maximumPoolSize被设为了Integer.MAX_VALUE,也就是可以无限创建,使用时要注意避免提交大量任务。

  • newScheduledThreadPool
    创建一个最大无限大的线程池,可以在一定时间后或定期执行任务。

了解了这些之后就可以很好的使用线程池了~

参考大神文章
http://www.jianshu.com/p/87bff5cc8d8c
https://www.oschina.net/question/565065_86540

0 0
原创粉丝点击