当我们创建线程时候,每创建一个线程,都会和操作系统进行交互,当我们需要很多线程的时候,频繁的创建线程会消耗很大的资源,为了提高性能,就需要引入“线程池”的概念了。
线程池在程序启动时就创建大量空闲线程,程序将一个 Runnable 对象传入线程池中,线程池就会分配一个线程去执行其 run 方法,当 run 方法体执行完,线程也并不会死亡,而是重新回到线程池中,等待下一个 Runnbal 对象传入。
Java 线程池框架有几个接口和实现类,他们的关系如下:
从图中可以看到 Executor、ExecutorService、ScheduledExecutorService 定义线程池接口,ThreadPoolExector 和 ScheduledThreadPoolExecutor 是线程池的实现,前者是一个普通的线程池,后者是一个定期调度的线程池,Executors 是辅助工具,用以帮助我们快速定义线程池。
newCachedThreadPool()
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
newCachedThreadPool(ThreadFactory threadFactory)
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程。
newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,在需要时使用提供的 ThreadFactory 创建新线程。
threadFactory:创建新线程时使用的工厂
nThreads:池中的线程数
newScheduledThreadPool(int corePoolSize)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
corePoolSize:池中所保存的线程数,即使线程是空闲的也包括在内。
threadFactory:创建新线程时使用的工厂
newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
newSingleThreadExecutor(ThreadFactory threadFactory)
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程,并在需要时使用提供的 ThreadFactory 创建新线程。
newSingleThreadScheduledExecutor()
创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
该方法会创建一个线程数量不定的线程池,其最大线程数为 Integer.MAX_VALUE ,这种线程池比较适合执行大量耗时较少的任务,当整个线程池处于限制状态时,线程池中的线程都会因为超时而被停止,这个时候它几乎不占用系统资源。
当传入 ThreadFactory 参数时,会使用 ThreadFactory 创建新线程。
public class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
threadPool.execute(new RunnableTest(i));
}
threadPool.awaitTermination(3, TimeUnit.SECONDS);
if (!threadPool.isShutdown()) {
System.out.println("关闭线程池");
threadPool.shutdown();
}
// 这个时候再调用线程池就会出错
// threadPool.execute(new RunnableTest(100));
}
}
class RunnableTest implements Runnable {
private int number;
public RunnableTest(int number) {
this.number = number;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getId() + " - " + number
+ " start");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getId() + " - " + number
+ " end");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在上面的代码中,特意将 awaitTermination 方法的时间设置为 3 秒,3 秒之后继续往下执行代码,所以执行结果是:
9 - 0 start
11 - 2 start
13 - 4 start
10 - 1 start
12 - 3 start
关闭线程池
11 - 2 end
9 - 0 end
13 - 4 end
10 - 1 end
12 - 3 end
该方法创建一个线程数固定的线程池,当线程处于空闲状态时,它们也不会被回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。因为线程池内线程不会被回收,所以它能更快的响应外界的线程请求。
当传入 ThreadFactory 参数时,会使用 ThreadFactory 创建新线程。
public class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
threadPool.execute(new RunnableTest(i));
}
threadPool.shutdown();
}
}
class RunnableTest implements Runnable {
private int number;
public RunnableTest(int number) {
this.number = number;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getId() + " - " + number
+ " start");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getId() + " - " + number
+ " end");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
因为我们设置了线程池内线程数为 3,所以执行结果如下:
9 - 0 start
10 - 1 start
11 - 2 start
10 - 1 end
9 - 0 end
11 - 2 end
9 - 3 start
11 - 4 start
9 - 3 end
11 - 4 end
该方法创建一个固定长度的线程池,返回一个 ScheduledExecutorService 对象,该对象支持定时的以及周期性的任务执行。
当传入 ThreadFactory 参数时,会使用 ThreadFactory 创建新线程。
public class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
ScheduledExecutorService threadPool = Executors
.newScheduledThreadPool(3);
for (int i = 0; i < 5; i++) {
threadPool.schedule(new RunnableTest(i), 3, TimeUnit.SECONDS);
}
threadPool.shutdown();
}
}
class RunnableTest implements Runnable {
private int number;
public RunnableTest(int number) {
this.number = number;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + " - " + number
+ " start");
System.out.println(Thread.currentThread().getId() + " - " + number
+ " end");
}
}
执行结果
11 - 2 start
9 - 0 start
10 - 1 start
9 - 0 end
11 - 2 end
10 - 1 end
11 - 3 start
9 - 4 start
11 - 3 end
9 - 4 end
我们执行线程时是调用了 ScheduledExecutorService 对象的 schedule 方法,并且设置延时时间为 3 秒,所以在 3 秒之后先执行了 3 个任务(因为线程池长度为 3),然后又执行剩下的 2 个任务。
该方法可以创建一个单线程化的 Executor,即只创建唯一的工作线程来执行任务,如果这个线程发生异常导致结束,会有另一个线程取代它,保证顺序执行。
单工作线程最大的特点是可以保证顺序的执行各任务,并且在任意给定的时间不会有多个线程是活动的。
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
threadPool.execute(new RunnableTest(i));
}
threadPool.shutdown();
}
}
class RunnableTest implements Runnable {
private int str;
public RunnableTest(int str) {
this.str = str;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("Thread:" + Thread.currentThread().getId()
+ " number:" + str);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出结果:
Thread:9 number:0
Thread:9 number:1
Thread:9 number:2
Thread:9 number:3
Thread:9 number:4
可以看到,每一次执行,都是调用了 id 是 9 的那个线程的 run 方法。
从名字上就可以看出,这个方法是 newSingleThreadExecutor 和 newScheduledThreadPool 的结合体,这里不详述...