对于一个线程来说,创建和销毁 是非常消耗时间和资源的。如果过于频繁的进行线程的创建和销毁,非常消耗系统资源,势必会影响系统性能。同样地,如果并发的线程过多,那么很多线程会因为抢占系统资源而导致阻塞。
为了解决这些问题,引入了线程池的概念。
线程池的思想大致如下:
- 准备一个存放任务的队列;
- 一次性地创建n个线程,启动线程。此时由于任务队列是空的,线程处于等待状态;
- 向任务队列中放入一个任务,就会有一个线程被唤醒,并执行任务;
- 线程执行完这个任务之后,继续等待下一个任务的到来;
- 若多个任务进入队列,则多个线程会被唤醒;
可以发现,在整个过程中,都是在循环利用一开始就创建的n个线程,大大减少了创建和销毁线程带来的系统开销。
Java中提供自带了线程池ThreadPoolExecutor,看一下他的一个构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
乍一看这么多花里胡哨的参数,其实理解起来并不困难,且听我慢慢道来。
- corePoolSize :表示核心线程的数目,假设该值为 5,那么相当于初始化了 5个线程在线程池中。如果线程池的线程数目小于 corePoolSize,那么创建的线程为核心线程;如果超过 corePoolSize,则创建的线程为非核心线程。核心线程在默认情况下会一直存在于线程池中,即使它处于闲置状态。也就是说,就算核心线程啥活不干,线程池还是得养着它。
- maximumPoolSize == 核心线程(corePoolSize) + 非核心线程;
表示线程池中线程总数的最大值,当核心线程不够用的时候,会创建非核心线程,所有线程的总数不超过 maximumPoolSize。 - keepAliveTime:表示该线程池中非核心线程闲置的时长。不同于核心线程,如果一个非核心线程处于闲置状态,且闲置时间超过了keepAliveTime,就会被销毁掉。
- TimeUnit unit:表示keepAliveTime的单位,属于一个枚举类型,分别是「 NANOSECONDS、MICROSECONDS 、MILLISECONDS 、SECONDS 、MINUTES 、HOURS 、DAYS 」
- BlockingQueue workQueue:表示线程池的任务队列。必须有任务进入到这个容器中,线程才能被唤醒。当所有的核心线程都在工作时,新进来的任务就会进入到队列中等待被执行;当任务进来的太多,导致队列满了,那么就会新建非核心线程来执行任务。
当然还有几种构造函数方法,涉及到6个或7个参数。不过一般都用这个5个参数的构造方法来创建线程池。
对线程池对象,使用execute方法,即可将任务加入到该线程池的队列中来,唤醒线程并执行。看一段代码:
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
threadPool.execute(new Runnable(){
public void run() {
// TODO Auto-generated method stub
System.out.println("task 1");
}
});
}
上面的代码中,设置该线程池核心线程为5个,线程最多10个,非核心线程闲置10秒后销毁,workQueue为LinkedBlockingQueue类型。
关于workQueue以及线程池的种类,可以参考一下这篇好的文章,链接: