这是【从 0 到 1 学习 Java 线程池】系列文章的第 壹 篇,该系列文章总共三篇,介绍了 Java 线程池的使用以及原理,并且最后会实现一个基本的线程池。本篇文章主要介绍了 Java 线程池以及它的使用。
线程池用于多线程处理中,它可以根据系统的情况,可以有效控制线程执行的数量,优化运行效果。线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。
在面向对象的编程过程中,创建对象和销毁对象是非常消耗时间和资源的。因此想要最小化这种消耗的一种思想就是『池化资源』。线程池就是这样的一种思想。我们通过重用线程池中的资源来减少创建和销毁线程所需要耗费的时间和资源。
线程池的一个作用是创建和销毁线程的次数,每个工作线程可以多次使用;另一个作用是可根据系统情况调整执行的线程数量,防止消耗过多内存。另外,通过线程池,能有效的控制线程的最大并发数,提高系统资源利用率,同时避免过多的资源竞争,避免堵塞。
一般的线程池主要分为以下 4 个组成部分:
许多服务器应用常常需要处理大量而短小的请求(例如,web 服务器,数据库服务器等等),通常它们收到的请求数量很大,一个简单的模型是,当服务器收到来自远程的请求时,为每一个请求开启一个线程,在请求完毕之后再对线程进行销毁。这样处理带来的问题是,创建和销毁线程所消耗的时间往往比任务本身所需消耗的资源要大得多。那么应该怎么办呢?
线程池为线程生命周期开销问题和资源不足问题提供了解决方案。我们可以通过线程池做到线程复用,不需要频繁的创建和销毁线程,让线程池中的线程一直存在于线程池中,然后线程从任务队列中取得任务来执行。而且这样做的另一个好处有,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
Java 中提供了实现线程池的框架 Executor,并且提供了许多种类的线程池,接下来的文章中将会做详细介绍。
Java 中的线程池是通过 Executor 框架实现的,该框架中用到了
,
- Executor
,
- Executors
,
- ExecutorService
,
- ThreadPoolExecutor
和
- Callable
、
- Future
这几个类。
- FutureTask
其中
的构造方法如下:
- ThreadPoolExecutor
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- Executors.defaultThreadFactory(), defaultHandler);
- }
其中:
Java 线程池的工作过程如下:
生成线程池使用的是 Executors 的工厂方法,以下是常见的 Java 线程池:
SingleThreadExecutor 是单个线程的线程池,即线程池中每次只有一个线程在运行,单线程串行执行任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- public static ExecutorService newCachedThreadPool() {
- return new ThreadPoolExecutor(0,Integer.MAX_VALUE,
- 60L, TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>());
- }
FixedThreadPool 是固定数量的线程池,只有核心线程,每提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列,直到前面的任务完成才继续执行。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- public static ExecutorService newFixedThreadPool(int nThreads) {
- return new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>());
- }
CachedThreadPool 是可缓存线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60 秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。其中,SynchronousQueue 是一个是缓冲区为 1 的阻塞队列。
- public static ExecutorService newCachedThreadPool() {
- return new ThreadPoolExecutor(0,Integer.MAX_VALUE,
- 60L, TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>());
- }
ScheduledThreadPool 是核心线程池固定,大小无限制的线程池,支持定时和周期性的执行线程。创建一个周期性执行任务的线程池。如果闲置, 非核心线程池会在
时间内回收。
- DEFAULT_KEEPALIVEMILLIS
- public static ExecutorService newScheduledThreadPool(int corePoolSize) {
- return new ScheduledThreadPool(corePoolSize, Integer.MAX_VALUE,
- DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
- new DelayedWorkQueue());
- }
我们可以通过 Executors 的工厂方法来创建一个线程池。但是我们该如何让线程池执行任务呢?
线程池最常用的提交任务的方法有两种:
- ExecutorService.execute(Runnable runable);
- FutureTask task = ExecutorService.submit(Runnable runnable);
- FutureTask<T> task = ExecutorService.submit(Runnable runnable,T Result);
- FutureTask<T> task = ExecutorService.submit(Callable<T> callable);
可以看出
开启的是有返回结果的任务,会返回一个
- submit
对象,这样就能通过
- FutureTask
方法得到结果。
- get()
最终调用的也是
- submit
,
- execute(Runnable runable)
只是将
- submit
对象或
- Callable
封装成一个
- Runnable
对象,因为
- FutureTask
是个
- FutureTask
,所以可以在
- Runnable
中执行。
- execute
下面的示例代码演示了如何创建一个线程池,并且使用它管理线程:
- public class MyThread extends Thread {
- @Override
- public void run() {
- System.out.println(Thread.currentThread().getName() + " is running.");
- }
- }
- public class TestSingleThreadExecutor {
- public static void main(String[] args) {
- //创建一个可重用固定线程数的线程池
- ExecutorService pool = Executors.newFixedThreadPool(2);
- //创建实现了Runnable接口对象
- Thread tt1 = new MyThread();
- Thread tt2 = new MyThread();
- Thread tt3 = new MyThread();
- Thread tt4 = new MyThread();
- Thread tt5 = new MyThread();
- //将线程放入池中并执行
- pool.execute(tt1);
- pool.execute(tt2);
- pool.execute(tt3);
- pool.execute(tt4);
- pool.execute(tt5);
- //关闭
- pool.shutdown();
- }
- }
运行结果:
- pool-1-thread-1 is running.
- pool-1-thread-2 is running.
- pool-1-thread-1 is running.
- pool-1-thread-2 is running.
- pool-1-thread-1 is running.
来源: http://www.tuicool.com/articles/euANZnJ