一、线程池
- 什么是线程池
- 介绍
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。 - 线程的执行流程
当任务来了之后,线程池的执行流程是:先判断当前线程数是否大于核心线程数?如果结果为 false,则新建线程并执行任务;如果结果为 true,则判断任务队列是否已满?如果结果为 false,则把任务添加到任务队列中等待线程执行,否则则判断当前线程数量是否超过最大线程数?如果结果为 false,则新建线程执行此任务,否则将执行线程池的拒绝策略。
-
线程的拒绝策略
当任务过多且线程池的任务队列已满时,此时就会执行线程池的拒绝策略。
线程池的拒绝策略默认有以下 4 种:
AbortPolicy:中止策略,线程池会抛出异常并中止执行此任务;
CallerRunsPolicy:把任务交给添加此任务的(main)线程来执行;
DiscardPolicy:忽略此任务,忽略最新的一个任务;
DiscardOldestPolicy:忽略最早的任务,最先加入队列的任务。默认的拒绝策略为 AbortPolicy 中止策略。
- 线程池的作用
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。如果使用线程池那么就可以减少每次启动、销毁线程的损耗。 - 如何创建线程池
JAVA中创建线程池主要有两类方法,一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用。另一类是通过ThreadPoolExecutor类进行自定义创建。
(1) 通过Executors类提供的方法创建线程池
- newFixedThreadPool
创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
private static void createFixedThreadPool(){
ExecutorService executorService= Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index=i;
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+" "+index);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
- newCachedThreadPool
创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
private static void createCachedThreadPool() {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(() -> {
// 获取线程名称,默认格式:pool-1-thread-1
System.out.println(Thread.currentThread().getName() + " " + index);
// 等待2秒
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
- newScheduledThreadPool
创建一个周期性的线程池,支持定时及周期性执行任务。
private static void createScheduledThreadPool() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
System.out.println(" 提交任务");
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.schedule(() -> {
// 获取线程名称,默认格式:pool-1-thread-1
System.out.println(" " + Thread.currentThread().getName() + " " + index);
// 等待2秒
sleep(2000);
}, 3);
}
}
- newSingleThreadExecutor
创建一个单线程的线程池,可保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
private static void createSingleThreadPool() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(() -> {
// 获取线程名称,默认格式:pool-1-thread-1
System.out.println(Thread.currentThread().getName() + " " + index);
// 等待2秒
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
(2)使用ThreadPoolExecutor创建线程池
优点:
- 可以实时获取线程池内线程的各种状态
- 可以动态调整线程池大小
这里使用ThreadPoolExecutor创建一个忽略最新任务的线程池,创建规则:
1.核心线程数为5
2.最大线程数为10
3.任务队列为100
4.拒绝策略为忽略最新任务
public class ThreadPoolTest {
//使用ThreadPoolExecutor创建线程池
public static void main(String[] args) {
//设置核心池大小
int corePoolSize = 5;
//最大能接收的线程
int maximumPoolSize=10;
//当前线程数大于corePoolSize、小于maximumPoolSize时,超出corePoolSize的线程数的生命周期
long keepActiveTime = 100;
//设置时间单位,秒
TimeUnit timeUnit = TimeUnit.SECONDS;
//设置线程池缓存队列的排队策略为FIFO,并且指定缓存队列大小为5
ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(5);
//创建ThreadPoolExecutor线程池对象,并初始化该对象的各种参数
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepActiveTime, timeUnit,workQueue);
//往线程池中循环提交线程
for (int i = 0; i < 15; i++) {
//创建线程类对象
MyTask myTask = new MyTask(i);
//开启线程
executor.execute(myTask);
//获取线程池中线程的相应参数
System.out.println("线程池中线程数目:" +executor.getPoolSize() + ",队列中等待执行的任务数目:"+executor.getQueue().size() + ",已执行完的任务数目:"+executor.getCompletedTaskCount());
}
//待线程池以及缓存队列中所有的线程任务完成后关闭线程池。
executor.shutdown();
}
}
//线程类
class MyTask implements Runnable {
private int num;
public MyTask(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println("正在执行task " + num);
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task " + num + "执行完毕");
}
}