您的位置:首页 > 编程语言 > Java开发

java中线程池

2012-11-02 21:33 176 查看
java中线程池的实现在jdk1.5以上版本提供了ThreadPoolExecutor类,该类继承了抽象类AbstractExecutorService,是接口

Executor的底层实现类。

那么这里首先了解下Executor。jdk文档中说明了Executor接口执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程。例如,可能会使用以下方法:

 Executor executor = anExecutor;

 executor.execute(new RunnableTask1());

 executor.execute(new RunnableTask2());

而不是为一组任务中的每个任务调用 new Thread(new(RunnableTask())).start()。由此可见Executor简化了线程的创建,调度、撤销等,降低了线程因创建和撤销而花费的系统开销。

ThreadPoolExecutor的完整构造方法是:

 

ThreadPoolExecutor(int corePoolSize,

                   int maximumPoolSize,

                   long keepAliveTime,

                   TimeUnit unit,

                   BlockingQueue<Runnable> workQueue,

                   ThreadFactory threadFactory,

                   RejectedExecutionHandler handler

                   )

下面对其参数进行说明:

corePoolSize: 线程池维护线程的正常数目,即线程池的正常大小。

maximumPoolSize:线程池维护线程的最大数量。当线程池满了(即线程池中有corePoolSize个线程了)同时缓冲队列也满了情况下,如果有新的任务来临,可以开辟新的线程来执行任务,但                 是总的线程数目不能超过maximumPoolSize

keepAliveTime: 线程池维护线程所允许的空闲时间。如果池中当前有多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止。从而减少了资源消耗。

unit: 线程池维护线程所允许的空闲时间的单位。即制定keepAliveTime的时间单位。

workQueue: 线程池所使用的缓冲队列。当有任务来临时,如果当前线程池的活动线程数目大于等于corePoolSize时,同时缓冲队列未满时,则将任务添加到该队列中。

handler: 线程池对拒绝任务的处理策略。所谓拒绝,即当前线程池中活动的线程数目等于maxinumPoolSize,同时队列满时,则线程池对任务拒绝。该处理策略可以自己编写,也可以使用jdk中提供的四种策略,注意这四种策略要慎重选择,选择不当可能导致你不想要的结果。

下面对该四种策略进行介绍:

1、ThreadPoolExecutor.AbortPolicy。该策略为默认的策略。即任务遭到拒绝时抛出运行时异常RejectedExecutionException。

2、ThreadPoolExecutor.CallerRunsPolicy。该策略绕过线程池,直接在添加任务的线程(即调用execute方法的线程,execute方法将在下面降到)执行被拒绝的任务,说实话我还不知道在什么情况下会用到该策略。

3、ThreadPoolExecutor.DiscardPolicy。该策略比较简单,直接丢掉被拒绝的任务。

4、ThreadPoolExecutor.DiscardOldestPolicy。该策略丢掉老任务,保留新任务。具体就是从任务缓冲队列头部删除没有被执行的任务,然后将新到的任务添加到队列中。

了解了它的构造函数以及相关参数以后以后,下面我们来看看怎么将任务添加到线程池的。

当有任务到达时,通过 execute(Runnable)方法被添加到线程池,这里可以看到所谓的任务其实就是实现了接口Runnable的类型对象,任务的具体工作其实就是在run()方法里执行。

当一个任务通过execute(Runnable)方法欲添加到线程池时,根据你线程池和所选择的拒绝策略对任务进行相应的处理,具体如下:

1、线程池中的任务数量小于corePoolSize,则任务直接被执行

2、线程池中的任务数量大于等于corePoolSize,同时workQueue未满,则将任务添加到缓冲队列workQueue中。

3、线程池中任务数量大于等于corePoolSize,workQueue同时满,但是线程池中运行的任务数目小于maxinumPoolSize时,则重新开辟线程执行新任务。

4、线程池中的任务数量大于等于corePoolSize,workQueue也满了,同时maxinumPoolSize也超了,那么新任务将被拒绝,并根据相应的拒绝策略来进行处理。相应的拒绝策略处理在上面有所介绍。

介绍完这些以后,下面我们看一个列子:

 

思考:

1、当新任务到达时,execute(Runnable)处理方式有四种情况,其中说到的第三种情况,是否会导致后来的任务先运行的情况。

   这里肯定会出现这种情况的,即第三种情况中,后来的任务会先于缓冲队列中的任务执行。通常我们都会让任务有个先来后,那么如何避免出现这种情况呢,其实我们可以设置corePoolSize=maxinumPoolSize。

2、关于缓冲队列的思考。

   通过查阅jdk文档知道BlockingQueue是个接口,支持两个附加操作的 Queue,这两个操作是:获取元素时等待队列变为非空,以及存储元素时等待空间变得可用。 那么它的具体实现有哪些呢?

(1)ArrayBlockingQueue,一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。个人不建议线程池中使用这种队列,因为线程池中涉及到频繁的删除插入操作(即出队入队操作),这样难免涉及到元素的移动,而这是一个消耗资源的操作。

(2)LinkedBlockingQueue,一个基于已链接节点的、范围任意的 blocking queue。此队列按 FIFO(先进先出)排序元素。队列的头部 是在队列中时间最长的元素。队列的尾部 是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。在线程池中本人侧重喜欢使用这种队列。

(3)SynchronousQueue,一种阻塞队列,其中每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然。也即队列中只有存在一个元素。

(4)DelayQueue,该队列是一个延迟队列,即队列中的每一个元素只有延迟期满之后才能被take出来。

(5)LinkedBlockingDeque,双向并发阻塞队列。所谓双向是指可以从队列的头和尾同时操作,并发只是线程安全的实现,阻塞允许在入队出队不满足条件时挂起线程。每一个结点有前后两个引用,这样才能将所有元素串联起来,支持双向遍历。

(6) PriorityBlockingQueue,要了解PriorityBlockingQueue我们首先了解PriorityQueue,那么PriorityQueue是什么队列呢?它是一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法。优先级队列不允许使用 null 元素。依靠自然顺序的优先级队列还不允许插入不可比较的对象(这样做可能导致 ClassCastException)。 此队列的头 是按指定排序方式确定的最小
元素。如果多个元素都是最小值,则头是其中一个元素——选择方法是任意的。队列获取操作 poll、remove、peek 和 element 访问处于队列头的元素。而PriorityBlockingQueue一个无界阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞获取操作。

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: