线程池的原理:从线程池中创建线程来处理线程任务,执行完又被线程池回收
线程池所在的包:java.util.concurrent
顶级接口是:Executor
真正的线程池接口是:ExecutorService
java.util.concurrent.Executor类提供创建线程池的方法。
用不同的方法可以构造不同的线程池:有以下4种
(1)可缓存的线程池
(2)单线程池
(3)固定长度的线程池
(4)固定长度且可控制执行时间的线程池
(5)自定义线程池
1,可缓存的线程池
方法:newCachedThreadPool()
生成线程池对象类型:ExecutorService
功能:执行任务时,生成一个线程执行任务,如果线程被占用且还有任务在等待执行,就创建新的线程来解决,执行完任务的线程,会继续执行新的任务,直到所有任务被执行完毕
代码演示:
public static void main(String[] args) {
//用Executors可直接调用newCachedThreadPool()方法,返回的是一个ExecutorService对象
//记得导入java.util.concurrent.ExecutorService包和java.util.concurrent.Executors包
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//然后可以在线程池中执行10个任务,可以调用execute方法,需要传入一个Runnable对象
for (int i = 0; i < 10; i++) {
cachedThreadPool.execute(new MyRunnable(i));
//不延迟产生的结果截然不同
//不加休眠时间,当有新的任务时,但头一个线程还没完成自己的任务,线程池自己就会创建新的线程来执行
//当头一个线程执行完自己的任务,空闲下来时,就会去执行剩余的任务
//这就是有缓存的线程池的好处,根据当前情况来做出最节约资源的操作,有休眠时间的情况下,一个线程就足以执行所有任务
//不必重复创建和销毁线程对象
try {
Thread.currentThread().sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
2,单一线程池
方法:newSingleThreadExecutor()
生成线程池对象类型:ExecutorService
功能:不管等待执行的任务有多少,只能有一个线程逐个执行完毕
代码演示:
public static void main(String[] args) {
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
//然后可以在线程池中执行10个任务,可以调用execute方法,需要传入一个Runnable对象
for (int i = 0; i < 10; i++) {
//SingleThreadPool()方法创建的线程池,就算没有时间延长,他也会等待这个线程执行完所有任务
//而不会创建新的线程来帮助完成,因为它只允许一个线程存在执行
singleThreadPool.execute(new MyRunnable(i));
3,固定长度的线程池
方法:newFixedThreadPool(int nThreads),nThreads是生成的长度参数
生成线程池对象类型:ExecutorService
功能:生成线程执行线程任务,如果有任务等待执行就继续生成线程,最大只能生成长度个数的线程,如果还有任务在等待,就等前面的线程执行完毕再来执行等待线程
代码演示:
public static void main(String[] args) {
//调用FixedThreadPool()这个方法,必须要设定长度
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
//然后可以在线程池中执行10个任务,可以调用execute方法,需要传入一个Runnable对象
for (int i = 0; i < 10; i++) {
try {
Thread.currentThread().sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//就算没有时间休眠,在一个线程执行不了这么多任务时,该线程会创建自身设置设置长度个数的线程
fixedThreadPool.execute(new MyRunnable(i));
4,固定长度且可控制执行时间的线程池
方法:newScheduledThreadPool(int corePoolSize) //corePoolSize是生成的线程池长度参数
生成线程池对象类型:ScheduledExecutorService
功能:可以生成最多线程长度个数的线程来执行任务,并且可以控制执行的时间,如第一个线程何时执行,每个线程执行时间的间隔,时间单位要声明清楚
代码演示:
public static void main(String[] args) {
//首先创建一个线程池,返回的是ScheduledExecutorService类型对象
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
System.out.println("*****************开始执行******************");
//这个方法会按照规定的时间间隔无限执行下去
//创建并执行一个在给定延迟之后启用的ScheduledFuture
// 参数:
// 命令执行任务。 new MyThread()
// 初始化延迟第一次执行的时间。 5
// 在连续执行之间的期间。 2
// 单元的初始延迟和周期参数的时间单位。 TimeUnit.SECONDS
5,自定义线程池的方法和要点
参数:
corePoolSize :核心线程数
maximumPoolSize :线程池最大线程数
keepAliveTime :表示线程没有任务执行时,保持多久会终止
unit :参数keepAliveTime的时间单位
workQueue :一个阻塞队列,用来存储等待执行的任务
threadFactory :线程工厂,主要用来创建线程
handler :表示当拒绝处理任务时的策略
想获得某个参数,都有相应的get方法
自定义线程池
corePoolSize 核心线程数
maximumPoolSize 最大线程数
keepAliveTime 线程没有任务执行会保持多长时间被销毁,核心线程除外
unit 时间的单位,跟上面的有关
workQueue 工作队列设置长度 new ArrayBlockingQueue<Runnable>(3)
返回池中当前线程数 —— executor.getPoolSize()
返回该执行器使用的任务队列 —— executor.getQueue()
返回已完成执行的任务的大致总数 —— executor.getCompletedTaskCount()
自定义线程工作原理:
(1)我们给入的任务, 首先由核心线程 处理
(2) 核心线程全部占用,剩余的任务放入 阻塞队列
(3)阻塞队列放满,就创建新的线程处理任务,最多 不能超过最大线程数
(4) 如果还有任务,就 拒绝执行 ,进行销毁
实例:假如有12个线程任务等待执行,核心线程为5个,最大线程数为7,阻塞队列为4,在200毫秒之后没有任务执行的线程被销毁
代码演示:
public class MyThread implements Runnable{
int num;//用来显示执行到了那个任务了
public MyThread(int num) {
super();
this.num = num;
}
@Override
public void run() {
System.out.println("正在执行第"+num+"个任务");
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("第"+num+"个任务执行完毕");
}
}
public class Test {
public static void main(String[] args) {
//创建一个自定义线程池
// 定义的顺序依次为1,核心线程数 2,最大线程数 3,线程保持的时间 4,保持时间的时间单位 5,阻塞队列的长度
ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 7, 300, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(4));
//循环12次执行任务
for (int i = 1; i <= 12; i++) {
//这个方法是将一个任务确认执行,并从线程池中取出线程来执行
executor. execute(new MyThread(i));
//最后显示每次执行的信息,线程中的线程数,核心线程数,队列等待数,的信息
//返回队列等待的时候,记得是返回等待的长度,所以记得加size()
System.out.println("线程中的线程数:"+ executor.getPoolSize()+",队列中等待执行的任务数:"+ executor.getQueue().size()+",已经执行完的任务数:"+ executor.getCompletedTaskCount());
}
//可以理解为关闭线程池
executor.shutdown();
}
}