技术开发 频道

J2SE5.0中的线程缓冲 ---- 线程池

【IT168 技术文档】

一、前言
    用Java编写多线程程序已经是一个非常简单的事了,不过与其它多线程系统相比,一些高级特性在Java中仍然不具备,然而在J2SE5.0中这一切将会改变。J2SE5.0增加大量的线程相关类使得编写多线程程序更加容易!

二、线程池-Thread Pools
    线程库的基本思想简单的讲就是,一个线程库中拥有一定数量的线程,当有任务要执行时,就从线程库中找一个空闲的线程来执行这个任务,任务执行完后,该线程返回线程库等待下一个任务;如果线程库中没有空闲的线程来执行该任务,这时该任务将要等待直到有一个空闲的线程来执行它。这听起来有点不爽,那么我们为什么还要使用线程库呢?

三、使用线程库的三大理由
    重用线程能够获得性能上的好处。在多线程环境中,创建一个线程要花很高的代价。线程库使得线程能够被重用,当有大量任务要执行时,线程库避免了不断创建与销毁线程所带来的系统开销,使用得你的程序整体运行效率得到了一定程度的提高。
    还有一个很重要的原因就是,线程库考虑到了较好的程序设计。如果有大量任务要执行,如果不用线程库,你不得不不重复创建一个线程、管理这个线程的生命同期的代码。断重复这些步骤是一件很乏味的工作,因为这与我们的业务逻辑(任务)无关。使用线程库,这一切将由线程库代为管理,你只需关注于你的商务逻辑,当要执行一个任务时,你只需简单的创建一个任务,并把它丢给线程库去执行就OK。
    最后一个也是最为重要的一个:当有大量任务需要同时执行时,线程库能够带来重大的性能提升。这一点看上去与第一点相似,事实上,任何时候活动的线程都比CPU数量多得多,因此线程库能够充分利用CPU的时间片,使得程序看去上运行较快且高效。

四、如何使用线程库

在使用线程库需要做两件事:创建任务及建立线程库本身。
一个任务是一个实现了Runnable或Callable接口的对象。
线程库则基于Executor接口。
java.util.concurrent.Executor


java.util.concurrent.ExecutorService


java.util.concurrent.ThreadPoolExecutor

package java.util.concurrent; public class ThreadPoolExecutor implements ExecutorService { public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler); }

五、  两个重要概念Sizes与Queues

Size
    一个线程库创建后,其的大小在最小值(corePoolSize)与最大值(maximumPoolSize)之间,运行时可根据getPoolSize()确定其当前的大小。
Queue
队列用于存放等候执行的任务。
下面一步步分析这两个值在线程库的工作中是如何运作:

1.   ThreadPoolExecutor tpe = new ThreadPoolExecutor(M, N, TIMEOUT, TimeUnit.MILLISECONDS,
2.            new LinkedBlockingQueue<Runnable>());
3.
4.        Task[] tasks = new Task[nTasks];
5.        for (int i = 0; i < nTasks; i++) {
6.            tasks[i] = new Task(n, "Task " + i);
7.            tpe.execute(tasks[i]);
8.        }
9.        tpe.shutdown();

    行1-2构造了一个线程库,M个core threads和N maximum threads,这时实际上并没有线程被创建。(可能过prestartAllCoreThreads() 和 prestartCoreThread() 方法分别预先创建)
第7行 一个任务被加入到线程库,这时下列5种情况之一将会发生:
如果库中的线程数少于M,线程库将立即启动一个新的线程来运行这个任务。即使库中有空闲的线程,仍然会产生一个新线程直到数量到达M。
    如果库中的线程数在M和N之间,并且只少有一个是空闲线程,那么任务将由这个空闲线程执行。
如果库中的线程数在M和N之间,并且没有空闲线程,这时库会检查存在的工作队列,如果任务能够放置在队列中而不被阻塞,那么任务就会放置在该列队中,不会有新的线程启动。
如果库中的线程数在M和N之间,并且没有空闲线程,且任务不能无阻塞地加入到队列中,这时库会开始一个新线程来运行这个任务。
    如果库中的线程数量已到达N且没有空闲线程,这时库将会试着放置新任务到一个队列。如果该队列已到达它的最大大小,任务将会被拒绝,添加失败,否则任务将被接受等待有空闲的线程来运行它。
一个任务执行完成,运行这个任务的线程将去运行队列中的下一个任务,如果队列中没有任务,将会发生下面两种情况之一:
    如果库中的线程数大于M,线程将等待一个队列中有新的任务。如果队列中的新任务没有超时,该线程将运行这个任务,否则线程将退出以减少库中的线程数量。超时值是在新构造线程库时指定的TIMEOUT,如果TIMEOUT为0,不管库的最小大小(corePoolSize)是多少,线程执行完任务后总是退出。
如果库中的线程数量等于M或小于M,该线程将不确定地被阻塞以等待一个新的任务被加入到队列(除非TIMEOUT=0,它将退出),当它有效时就会运行这个新任务。

0
相关文章