线程池
为了避免频繁的创建线程和销毁线程带来的消耗,我么创建线程池,让线程进行复用,在线程池中总有几个活跃的线程在工作,当有新任务提交时,空闲线程就可以去执行,而不必新建线程,当任务执行完毕不是销毁线程,而是把他归还给线程池,以便复用。
Executor框架
在J.U.C并发包中JDK提供了几个支持线程池的核心类,如下:
(1)java.util.concurrent.Executors
这个类扮演着线程池工厂的校色,有几个静态方法,用来创建线程池
public static ExecutorService newFixedThreadPool(int nThread): 创建指定线程数的线程池,该线程池中的线程数量不会改变,当有任务,有空闲线程则执行,没有任务进入任务队列,等待有线程空闲就执行
public static ExecutorService newSingleThreadExecutor() :创建一个只有一个线程的线程池,每次有新任务,若线程空闲则执行,不空闲进入队列,等待空闲
public static ExecutorService newCachedThreadPool():创建一个不固定线程数量的线程池,每次有新任务,若线程空闲则执行,不空闲就新创建一个线程进行执行
可以看到这个创建线程池的方法返回的都是ExecutorService对象,也叫线程池对象
(2)java.util.concurrent.ExecutorService
ExecutorService是一个接口,实现了Executor接口,ExecutorService这个接口中提供submit(Runnable task)这个方法,用于提交任务。shutdown()方法,关闭线程池
(3)java.util.concurrent.Executor
这个接口只有一个方法void execute(Runnable command)也是用于提交Runnable对象(任务)
(4)java.util.concurrent.ThreadPoolExecutor
在Executors类中的创建线程池的方法,都是包裹着ThreadPoolExecutor类创建线程池对象的方法,以newFixedThreadPool(int nThread)方法为例,这个方法的源代码如下:
也就是创建线程池的内部实现是在ThreadPoolExecutor类中。下面我们将详细分析线程池实现的原理
核心线程池的实现原理
在ThreadPoolExecutor类中通过构造方法来创建线程池,有如下几个重载方法
以最后一个为例分析线程池参数的含义
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:线程池中目前的线程数
maximumPoolSize:线程池中最大允许的线程数
keepAliveTime:当线程池中的线程数大于 corePoolSize,空闲线程的存活时间
unit:存活时间的单位,比如TimeUnit.SENCOND
workQueue:任务的提交队列,若没有空余线程,任务进入这个队列
threadFactory:线程创建工厂,线程池已有的线程就是通过这个工厂创建的,一般使用默认工厂
handler:拒绝策略,当线程池池中的线程数大于最大线程数,拒绝新任务的策略
那么比如newFixedThreadPool(int nThread)其实就是让corePoolSize=maximumPoolSize来实现固定线程数量的线程池
实例演示线程池的用法:
package xidian.lili.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static class myTask implements Runnable{
@Override
public void run() {
System.out.println(System.currentTimeMillis()+":"+Thread.currentThread().getId()+"thread");
try {
Thread.sleep(1000);//每个线程停顿1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
myTask task=new myTask ();
//创建线程池,有5个线程
ExecutorService exec=Executors.newFixedThreadPool(5);
//提交10个任务
for(int i=0;i<10;i++){
exec.submit(task);
}
exec.shutdown();//关闭线程池
}
}
思想(1)创建Runnable对象(任务)
(2)提交给线程池
我们newFixedThreadPool(5)创建5个线程大小的线程池,提交10个任务,5个线程一次运行然后再一起复用,时间刚好相差一秒