三、Executor
Java SE 5以后,提供了Executor来管理Thread对象。Executor在客户端和任务执行之间提供了一个间接层,与Thread直接执行任务不同,这个中介对象将执行任务。有了Executor就不需要显示的管理线程的生命周期。
ExecutorService(具有服务周期的Executor)能够构建其阿丹的上下文来执行Runnable对象中的任务。
通常通过Executors的三个静态工厂方法来创建ExecutorService对象。
1、newCachedThreadPool();
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to <tt>execute</tt> will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* 创建一个线程池,当需要新创建一个线程时,线程池就会创建新的线程。
* 如果先前创建的线程可用(空闲),则线程池会复用先前的线程,此时不需要
* 创建新的线程。这种线程池典型的适合于一个拥有许多的短生命周期线程的程序。
* 如果没有可用的线程,则线程池会创建一个新的线程并加入线程池,如果线程
* 空闲时间超过60秒,则该线程会被终止,并被移除线程池,释放资源。
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
此线程池只会在需要线程去执行任务时才会去创建或复用线程。
用Executor执行任务。public class CacheThreadPool {
public static void main(String[] args) {
ExecutorService exe = Executors.newCachedThreadPool();
for(int i = 0; i < 5; i++){
exe.execute(new LiftOff());
}
exe.shutdown();
}
}
shutdown()方法:防止新任务提交给这个Executor,即这个Executor不接受新的任务,之后再执行Executor.execute(Runnable run)方法不能执行任务,但是,在shutdown之前提交的任务还是会执行,当所有任务的执行完毕时,程序将会尽快退出。
2、newFixedThreadPool()
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* <tt>nThreads</tt> 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}.
*
* 创建包含有固定线程数(nThread)的线程池,并且维护一个共享的等待队列。
* 如果一个新加入的任务在提交时,所有的线程都是活动的,那么这个任务将被加入到
* 一个等待队列,直到有线程空闲为止。如果一个线程因为在执行时期因为错误而被关掉并终止,
* 此时,如果等待队列中还有未执行的任务,则线程池会创建一个新的线程来代替原来关闭的线程。
* 线程池里的线程会一直存在,除非被明确的关闭。
* @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>());
}
public class FixedThreadPool {
public static void main(String[] args) {
ExecutorService exe = Executors.newFixedThreadPool(5);
for(int i = 0; i < 5; i++){
exe.execute(new LiftOff());
}
exe.shutdown();
}
}
3、newSingleThreadPool()
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute 任务会被安全的已序列化的方式执行
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* <tt>newFixedThreadPool(1)</tt> the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
此线程池就是线程数为1的FixedThreadPool.如果给这个SingleThreadPool提交了多个任务,则这些任务将按提交时间顺序排队,一个一个的执行。
一个任务执行,只有当他前面的任务执行完毕之后才能开始。即会序列化提交给他的任务,并维护自己的悬挂任务队列。
如果有多个任务需要处理同一个资源,且多个任务不能同时更改资源,可以用此线程池来处理,从而可以避免在共享资源上做同步处理。
public class SingleThreadExecutor {
public static void main(String[] args) {
ExecutorService exe = Executors.newSingleThreadExecutor();
for(int i = 0; i < 5; i++){
exe.execute(new LiftOff());
}
exe.shutdown();
System.out.println("I LOVE YOU");
}
}
从源码中可以看出,其实这三个方法返回的都是同一类对象,只是这三个方法在创建这类对象时的初始化参数不一样而已。
创建对象的方法为::
new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
这个构造器里的
第一个参数是:构造的线程池中的核心线程数,所谓的核心线程可以简单的理解为线程池中最少需要拥有的线程。
第二个参数是:构造的线程池中最大的线程数。即该线程池最多拥有的线程数,如果线程池中已拥有的线程数等于这个最大值,并且所有线程都是活动的,此时如果提交给该线程池任务,则该任务会被挂起,加入到悬挂任务队列。
第三个参数是:一个long值,表示超时的数值。
第四个参数是:超时的时间单位。第三个和第四个一起,即线程空闲多久时间单位后,该线程会被终止。
第五个参数是:等待处理的任务队列,即悬挂任务队列。
ThreadPoolExecutor类中包含有线程池的一些统计信息方法,如最大线程数,核心线程数等,也包含一些线程的管理方法。
所有的上述三个方法都有一个重载的指定ThreadFactory的方法,这些重载方法的作用是用指定的线程工厂生成加入线程池的线程。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}