1.java线程池原理

用一个线程A去维护一个用于装载多个线程的“池子”,这个“池子”可能是队列或者链表;用另一个线程B去做“池子”里面线程要做的事情。当线程A发现“池子”里面有线程加入,就会去notifyAll(),告诉线程B,线程B就去查询这个池子的线程并执行其内容,执行完继续查询是否还有线程需要执行,有继续执行,没有等待。

2.线程池Executor的介绍

2.1线程池介绍和功能

我们知道,创建和销毁对象(例如线程),是存在开销的(java的内存分配和GC机制),如果应用中频繁出现线程的创建和销毁,那么会影响到应用的性能。好在我们使用Java Executor 框架可以通过线程池等机制解决这个问题,改善应用的体验。这是java1.5版本引入。可以理解为创建一些线程后,它们的集合就是线程池。当收到执行请求时,线程池中会取出一个空闲的线程为之服务,服务完不关闭该线程,而是将该线程放回线程池中。

在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在获取到任务后,它就在内部找有无空闲的线程,再把任务交给空闲的线程,这也反映出封装的思想。记住,任务是提交给整个线程池,一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。

Executor 框架为开发者提供了如下能力:

  • 创建工作线程池,同时通过队列来控制能够在这些线程执行的任务的个数。
  • 检测导致线程意外终止的错误。
  • 等待线程执行完成并获取执行结果。
  • 批量执行线程,并通过固定的顺序获取执行结果。
  • 在合适的时机启动后台线程,从而保证线程执行结果可以很快反馈给用户。

2.2 线程池内部各类关系

Executor

Executor 框架的基础是一个名为Executor 的接口定义,Executor 的主要目的是分离任务的创建和执行,最终实现上述所说的功能点。

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

开发者可以通过实现Executor 接口并重写execute()方法从而实现自己的Executor 类。当然Java 本身也为我们提供了预定义的线程池实现。

Executors

Executor 的工厂类,可以用它来创建预定义的线程池实现。该工厂类包含如下几个静态工厂方法(工厂设计模式)来创建线程池:

  • newSingleThreadExecutor():创建一个只有但线程的线程池,这个线程池中永远只有一个线程来串行执行任务队列中的任务。同时也可以实现线程死掉后创建新线程来执行任务。
  • newFixedThreadPool(int nThread):创建一个可复用的、固定线程数的线程池。n代表线程池中线程的个数。
  • newCachedThreadPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓存在线程池中。当有新的任务需要执行时,线程池会创建新的线程来处理它,空闲的线程会等待60秒来执行新任务,当没有任务可执行时就自动销毁,因此可变大小线程池会根据任务队列的大小而变化。
  • newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务。corePoolSize指池中所保存的线程数,即使线程是空闲的也被保存在线程池中。
  • newSingleThreadScheduledExecutor():创建只有一个线程的线程池,它可以在指定延迟后执行线程任务。

上面5个方法中的前3个方法返回的是一个ExecutorService对象,该对象代表一个线程池,它可以执行Runnable对象或Callable对象所代表的线程;后2个方法返回ScheduledExecutorService线程池,它是ExecutorService的子类,它可以在指定延迟后执行线程任务。

ExecutorService

该类是一个接口,是尽快执行线程的线程池(只要线程池中有空闲线程,就把任务交给线程立即执行)。当用完一个线程池后,应该调用该线程池的shutdown()方法,该方法将启动线程池的关闭序列,调用shutdown()方法后的线程池不再接收新任务,但会将以前所有已提交任务执行完成。当线程池中的所有任务都执行完成后,池中所有的线程都会死亡;另外也可以调用线程池的shutdownNow()方法来关闭线程池,该方法试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

ScheduledExecutorService

该类是一个接口,是可在指定延迟后或周期性的执行线程任务的线程池,即设置定时任务的线程池。

ThreadPoolExecutor

是ExecutorService的实现类,线程池的具体实现。Executor、ExecutorService和ThreadPoolExecutor中的方法共同管理线程池。预定义的线程池都是基于ThreadPoolExecutor类之上构建的,而通过ThreadPoolExecutor开发者可以自定义线程池的一些行为。

下面介绍ThreadPoolExecutor 的构造方法,了解各个参数的含义,因为这些参数直接影响到线程池的功能特性:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

corePoolSize

线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreeadPoolExecutor的allowCoreThreadTimeOut 属性设置为true, 那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由 keepAliveTime 所指定,当等待时间超过 keepAliveTime 所指定的时长后,核心线程就会被终止。

maximumPoolSize

线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。

keepAliveTime

非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。当ThreadPoolExecutor的 allowCoreThreadTimeOut 属性设置为true时,keepAliveTime同样作用于核心线程。

unit

用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)等。

workQueue

线程池中的任务队列,通过线程池的execute()方法提交的Runnable对象会存储在这个参数中

threadFactory

线程工厂,为线程池提供创建新线程的功能。threadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r)

2.3 ThreadPoolExecutor执行任务时的大致规则

  1. 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
  2. 如果线程池中的线程数量已经达到或者超出核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
  3. 如果在步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
  4. 如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler 的rejectedExecution方法来通知调用者。

3.示例代码

        ExecutorService service = Executors.newFixedThreadPool(3);
        for(int x = 0;x<10;x++){
            final int taskID = x;
            service.execute(new Runnable() {
                @Override
                public void run() {
                    for(int i = 0;i < 10;i++){
                        System.out.println(Thread.currentThread().getName() + ";loop of " + i  + "; task is " +taskID);
                    }
                }
            });
        }
        service.shutdown();
        Executors.newScheduledThreadPool(3).schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis() + "#");
                System.out.println("bombing!!");
                System.out.println(System.currentTimeMillis() + "%");
            }
        }, 10, TimeUnit.SECONDS);
        System.out.println(System.currentTimeMillis() + "&");

4.线程池使用步骤

  1. 调用Executors 类的静态工厂方法创建一个ExecutorService 对象,该对象代表一个线程池。
  2. 创建Runnable 实现类或Callable 实现类的实例,作为线程执行任务。
  3. 调用ExecutorService 对象的submit()方法来提交Runnable或Callable实例。
  4. 当不想提交任何任务时,调用ExecutorService 对象的shutdown()或shutdownNow()方法来关闭线程池。

results matching ""

    No results matching ""