目录
前言
线程是为了解决进程太重的问题,操作系统中进程的创建和销毁需要较多的系统资源,用了轻量级的线程来代替部分线程,但是如果线程创建和销毁的频率也开始提升到了一定程度,系统的开销同样也不可忽视了,为了解决这种问题,我们使用了线程池进行优化。本篇文章就主要来讲讲线程池。
1. 概念
我们提前把线程创建好,放到一个"池子"里,这就构成了一个线程池,我们在申请线程时,直接从这个所谓的池子里取,用完了之后再放回去,这就是线程池的基本思想,很好的减小了创建和销毁的线程的开销。
2. 线程池相关参数
这里主要介绍一下线程池构造方法中的相关参数。
Java中提供了线程池的类:ThreadPoolExecutor,在创建这个类的实例时,有很多参数,这里来简要介绍一下:

官方文档中,有这四种构造方式,我们介绍第四个,其中包含了前三个的参数。
我们先看前两个参数:
第一个表示核心线程数,第二个表示总数。
在Java的线程池中,把线程分为两种:核心线程和临时线程,一创建就申请的线程是核心线程,将任务交给核心线程。若核心线程被占用满,处理不过来,就会申请一些临时的线程来进行"协助",当空闲下来时,这些临时线程被释放,但是核心线程不会被释放。
接下来看:
这里第一个表示线程的存活时间,第二个表示时间单位。
通过这两个参数,来设定临时线程的存活时间。
接下来:
这里的参数是一个阻塞队列,存放着线程池的任务队列, 线程池就是从这个队列中取出任务,分配给其创建的线程。
继续看下一个参数:
这是一个线程工厂,这里的线程工厂使用了工厂设计模式,这是众多设计模式中很常见的一种,工厂模式用于弥补构造方法里存在的缺陷,比如说在创建多个构造方法的时候,如果参数的数量一致,那么参数的类型需要不同,但是实际中常常需要上述创建这种情况的构造方法,这里的参数含义不同,但是类型和数量却是一致,这样构造会编译出错。
所以为了解决这种问题,我们就引用了工厂设计模式,在这个设计模式里,我们不用构造方法来初始化对象,使用静态方法来初始化对象,即创建一个工厂类,在里面构造静态方法,来间接的达到创建对象的目的。
回到线程工厂,我们使用这个线程工厂,则是用来对线程池所创建出来的线程进行初始化的设定。
下面看最后一个参数:
此参数叫做拒绝策略,当线程池的任务队列满的时候,如果再次添加新任务,则会根据给出的拒绝策略来进行处理,文档提供了四种拒绝策略。
第一个叫做直接终止,即抛出异常,终止程序。
第二个是让调用者自己来执行任务。
第三个是丢弃队列中最老任务。
第四个是丢弃队列中最新任务。
3. Executors的使用
Executors是标准库中提供的一个线程池的简化版本,即对ThreadPoolExecutot进行了封装。
我们可以使用Executor来创建一些线程池:
可以看到这里有各种各样的线程池。
下面介绍四个常用的:
java
public static void main(String[] args) {
//固定线程数目的线程池,核心线程和最大线程数都是4
ExecutorService service = Executors.newFixedThreadPool(4);
//核心线程数为0,最大线程数为Integer.MAX_VALUE的线程池
ExecutorService service2 = Executors.newCachedThreadPool();
// 只有一个线程的线程池,核心线程数和最大线程数都是1
ExecutorService service3 = Executors.newSingleThreadExecutor();
// "定时器",核心线程数为0,最大线程数为10的线程池,在这个线程池的任务可以在一定时间后执行或定期执行
ExecutorService service4 = Executors.newScheduledThreadPool(10);
}
一般来说我们使用最多的线程池是前两个,接下来展示如何添加任务:
java
for (int i = 0; i < 100; i++) {
int id = i;
service.submit( new Runnable(){
@Override
public void run() {
System.out.println("线程池中的线程正在执行任务" + id);
}
});
}
运行效果:
总结
以上是对线程池的一些基本介绍,希望能够让大家了解Java中线程池各参数的作用并且能够使用Executors创建常见的线程池。