java并发:线程池之Executor、ExecutorService

一、序言

当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创建线程会降低系统的效率,因为创建和销毁线程均需要一定的时间。

线程池可以使线程得到复用,所谓线程复用就是线程在执行完一个任务后并不被销毁,该线程可以继续执行其他的任务。

java.lang.concurrent包中的Executors类为我们创建线程池提供了方便。

二、Executors的简单示例

此处我们先来看一个简单的例子,如下:

package com.soft;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorsDemo {
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
//      ExecutorService executor = Executors.newSingleThreadExecutor();
//      ExecutorService executor = Executors.newCachedThreadPool();
        ExecutorService executor = Executors.newFixedThreadPool(5);
        Thread.sleep(5*1000);//方便监控工具能捕获到
        for (int i = 0; i < 10; i++) {
            final int no = i;
            Runnable runnable = new Runnable() {
                public void run() {
                    try {
                        System.out.println("into" + no);
                        Thread.sleep(1000L);
                        System.out.println("end" + no);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            executor.execute(runnable);
        }//End for
        
        executor.shutdown();
        System.out.println("Thread Main End!");
    }
}

其运行结果如下:

into0
into3
Thread Main End!
into4
into1
into2
end0
into5
end3
end1
end4
into8
into6
into7
end2
into9
end5
end7
end8
end6
end9

解说:

ExecutorService有一个execute()方法,方法的参数是Runnable类型,通过该方法可将一个任务添加到线程池。

上面这个例子在任意某一时刻只有5个线程在执行,这是因为上述代码通过Executors.newFixedThreadPool(5)创建了一个固定长度的线程池(长度为5)。

三、Executors提供的线程池

Executors是线程的工厂类,也可以说是一个线程池工具类,它调用其内部静态方法(如newFixedThreadPool()等)即可创建一个线程池。

Executors提供不同的线程池机制,请参照下面这段摘录:

 

Executors提供的两个newFixedThreadPool方法如下:

    /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

 

    /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue, using the provided
     * ThreadFactory to create new threads when needed.  At any point,
     * at most {@code nThreads} threads will be active processing
     * tasks.  If additional tasks are submitted when all threads are
     * active, they will wait in the queue until a thread is
     * available.  If any thread terminates due to a failure during
     * execution prior to shutdown, a new one will take its place if
     * needed to execute subsequent tasks.  The threads in the pool will
     * exist until it is explicitly {@link ExecutorService#shutdown
     * shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @param threadFactory the factory to use when creating new threads
     * @return the newly created thread pool
     * @throws NullPointerException if threadFactory is null
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

 

四、Executor、ExecutorService

Executor、ExecutorService之间的关系如下图所示:

五、ExecutorService的生命周期

在本文最开始的那个示例中,有一句代码,如下:

executor.shutdown();

ExecutorService的实现类——ThreadPoolExecutor中的shutdown方法的实现如下:

    /**
     * Initiates an orderly shutdown in which previously submitted
     * tasks are executed, but no new tasks will be accepted.
     * Invocation has no additional effect if already shut down.
     *
     * <p>This method does not wait for previously submitted tasks to
     * complete execution.  Use {@link #awaitTermination awaitTermination}
     * to do that.
     *
     * @throws SecurityException {@inheritDoc}
     */
    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }

 

该语句并不是终止线程的运行,而是禁止在这个executor中添加新的任务,下文描述了该语句对于ExecutorService的意义。

线程池的状态

  • RUNNING

接受新任务并且处理阻塞队列里的任务。

  • SHUTDOWN

拒绝新任务但是处理阻塞队列里的任务。

  • STOP

拒绝新任务并且抛弃阻塞队列里的任务,同时会中断正在处理的任务。

  • TIDYING

所有任务都执行完(包含阻塞队列里面的任务)后,当前线程池活动线程数为 0,则调用 terminated 方法。

  • TERMINATED

终止状态。

 

线程池状态转换

  • RUNNING -> SHUTDOWN

显式调用 shutdown()方法或者隐式调用了 finalize()方法里面的 shutdown()方法。

  • RUNNING 或 SHUTDOWN -> STOP

显式调用 shutdownNow() 方法。

  • SHUTDOWN -> TIDYING

当线程池和任务队列都为空时。

  • STOP->TIDYING

当线程池为空时。

  • TIDING-> TERMINATED

当hook方法 terminated() 执行完成时。

posted @ 2016-04-13 11:31  时空穿越者  阅读(6145)  评论(1编辑  收藏  举报