jdk5.0并发包线程池的实现机制

 Jdk5.0以后提供了一个全新的线程运行控制机制,其实现被封装在java.util.concurrentjava.util.concurrent.atomicjava.util.concurrent.locks三个包中,实现了执行器、异步I/O、线程池、阻塞队列、时间调度、并发控制集合等功能。

线程池是我们常用到的功能之一,顾名思义,线程池就是存放线程的缓冲池,在一般的程序设计中,为了提高性能或处理较为复杂耗时的操作,我们一般开多个线程。线程池的目的就是管理这些线程,控制其生命周期以及任务分配等。

在写较深入的线程程序之前,我们一般会问,我们的任务(实现RunnableCallable接口的类对象)被提交后是如何执行的,线程是如何被启动及终止的,线程池中的线程数是固定的吗,是一个线程运行一个任务还是一个线程运行多个任务的呢?

下面我们结合jdk的源代码来分析一下线程池的实现模型。

1.      生命周期

线程池的生命周期一共存在四种运行状态标志,分别为RUNNINGSHUTDOWNSTOPTERMINATED,定义如下:

RUNNING:接受新任务并处理已排队(提交的任务是被放在一队列中)任务

SHUTDOWN:不接受新任务,但是处理已排队任务

STOP:不接受新任务,不处理已排队任务,并终止正在运行的任务

TERMINATED:STOP相同,并加上所有任务均已终止

下面是各个状态之间的转换:

RUNNING -> SHUTDOWN:调用shutdown()函数

(RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()函数

SHUTDOWN -> TERMINATED:当任务队列和线程池均为空

STOP -> TERMINATED:当线程池为空

这个要注意一下,在调用执行器提交完任务之后,一定要调用执行器的shutdown()或者shutdownNow()方法来终止线程池,不然的话即使所有任务处理完毕,虚拟机也不会退出。

        2.  调度过程

      无论怎么写程序,线程池也是有其执行器创建的,并在其构造函数中确定了线程池大小,下面是ThreadPoolExecutor的构造函数:

public ThreadPoolExecutor(int corePoolSize,

                              int maximumPoolSize,

                              long keepAliveTime,

                              TimeUnit unit,

                              BlockingQueue<Runnable> workQueue,

                              ThreadFactory threadFactory,

                              RejectedExecutionHandler handler) {…}

corePoolSize:核心线程池的大小,即即使线程空闲,也会保持这个数目

maximumPoolSize:线程池的最大大小,一般是Integer.MAX_VALUEcorePoolSize必须小于或等于这个数

workQueue:任务被执行之前放在这个队列中,线程要执行任务时从这个队列中取。注意BlockingQueue已经内置了同步机制,队列满或空均会使其处于对待状态。

其他参数不再说明。

通过上面大家可以看到线程和任务是分开存放管理的,这一点是理解线程池的关键。

下面我们通过一个函数看一下线程是如何被创建和启动的:

 

private void delayedExecute(Runnable command) {

              //如果运行状态为SHUTDOWN,拒绝此命令

        if (isShutdown()) {

            reject(command);

            return;

        }

        //如果当前线程数小于核心线程数,则创建并启动一个线程

        if (getPoolSize() < getCorePoolSize())

            prestartCoreThread();

 

              //把任务加入到任务队列中

        super.getQueue().add(command);

}
那么线程是如何执行任务的呢:

    总体来说,提交的任务按照提交先后顺序被放入任务队列,线程启动后从队列中取出任务执行,任务被取出后即被从队列中删除。由于操作系统的调度和每个任务的运算量不同,因此单个线程取出的任务并没有顺序,但是一定是队列的头,每个线程都是平等的,不允许取头后面的任务。

             下面是具体代码:

               

public void run() {

                     try {

                               Runnable task = firstTask;

                               firstTask = null;

                               //注意下面这个while循环,只要有任务就不会被终止,

                               //getTask()是取出任务队列的头,如果队列为空,进入阻塞状态

                               //runTask()是运行这个任务

                               while (task != null || (task = getTask()) != null) {

                               runTask(task);

                               task = null;

                        }

                    } finally {

                      workerDone(this);

                   }

              }

 

    更多的具体细节,请大家看ThreadPoolExecutorScheduledThreadPoolExecutor类  的源代码,两者都位于java.util.concurrent包中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章