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执行任务时的大致规则
- 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
- 如果线程池中的线程数量已经达到或者超出核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
- 如果在步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
- 如果步骤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.线程池使用步骤
- 调用Executors 类的静态工厂方法创建一个ExecutorService 对象,该对象代表一个线程池。
- 创建Runnable 实现类或Callable 实现类的实例,作为线程执行任务。
- 调用ExecutorService 对象的submit()方法来提交Runnable或Callable实例。
- 当不想提交任何任务时,调用ExecutorService 对象的shutdown()或shutdownNow()方法来关闭线程池。