线程池
基础知识
线程创建和启用会消耗系统资源,因此统一创建、管理和使用线程就显得非常有意义。我们把线程创建好好后放入线程池,需要时从这个池子中申请使用,使用完成后再还给线程池,线程的创建、管理、使用和消亡过程由线程池管理,这就是线程池的概念。
线程池主要核心原理:
①创建一个池子,池子中是空的
②提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子
下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
③但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
线程池代码实现步骤
1,创建线程池
2,提交任务
3,所有的任务全部执行完毕,关闭线程池
线程池使用:
Executors工具类
Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。
方法名称 | 说明 |
public static ExecutorServicenewCachedThreadPool() | 创建一个没有上限的线程池 |
public static ExecutorService newFixedThreadPool(int nThreads) | 创建有上限的线程池 |
Executors的执行原理:
①创建一个池子,池子中是空的
②提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
③但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo {
public static void main(String[] args) {
/*
public static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池
public static ExecutorService newFixedThreadPool(int nThreads) 创建有上限的线程池
*/
// 1.获取线程池对象
ExecutorService pool1 = Executors.newCachedThreadPool();
//ExecutorService pool1 = Executors.newFixedThreadPool(3); // 规定最大线程数量的池子
// 2.提交任务
// 第一种方式:直接实现方法接口
/*
pool1.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
});
*/
// 第二种方式:传入Runnable接口或Callable实现类对象
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
// 3.销毁线程池
pool1.shutdown();
}
}
任务代码:
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
//System.out.println(Thread.currentThread().getName() + "---");
}
}
}
自定义线程池ThreadPoolExecutor
用饭店的场景来理解线程池
核心线程都在忙,且等待队伍满,还有任务提交则会创建临时线程开始处理这些新提交的任务。这里需要注意的是,原先已经在排队的任务不会被临时线程优先处理,而是继续等待核心线程忙完才会轮到排队的线程。如果临时线程也被用完,再提交的任务就会被抛弃(拒绝服务)。
任务拒绝策略
任务拒绝策略 | 说明 |
ThreadPoolExecutor.AbortPolicy | 默认策略:丢弃任务并抛出RejectedExecutionException异常 |
ThreadPoolExecutor.DiscardPolicy | 丢弃任务,但是不抛出异常这是不推荐的做法 |
ThreadPoolExecutor.DiscardoldestPolicy | 抛弃队列中等待最久的任务然后把当前任务加入队列中 |
ThreadPoolExecutor.CallerRunsPolicy | 调用任务的run()方法绕过线程池直接执行 |
不断的提交任务,会有以下三个临界点:
①当核心线程满时,再提交任务就会排队
②当核心线程满,队伍满时,会创建临时线程
③当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略
使用线程池的代码:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyThreadPoolDemo1 {
public static void main(String[] args) {
/*
ThreadPoolExecutor threadPoolExecutor =new ThreadPoolExecutor
(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
参数一:核心线程数量 不能小于θ
参数二:最大线程数 不能小于等于θ,最大数量>=核心线程数量
参数三:空闲纸程最大存活时间 不能小于θ
参数四:时间单位 用TimeUnit指定
参数五:任务队列 不能为null
参数六:创建线程工厂 不能为null
参数七:任务的拒绝策略 不能为null
*/
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3, //核心线程数量,不能小于0
6, //最大线程数,不能小于0,最大数量>=核心线程数量
60, //空闲线程最大存货时间
TimeUnit.SECONDS, //时间单位
new ArrayBlockingQueue<>(3), //任务队列
Executors.defaultThreadFactory(), //创建线程工厂
new ThreadPoolExecutor.AbortPolicy() // 任务的拒绝策略
);
// 为线程池提交任务
pool.submit(new Runnable() {
@Override
public void run() {
// 任务执行代码
}
});
}
}
线程池多大合适?
I/O密集型可以使用thread dump进行辅助计算。