【JAVA入门】Day48 - 线程池
【JAVA入门】Day48 - 线程池
文章目录
- 【JAVA入门】Day48 - 线程池
- 一、线程池的主要核心原理
- 二、自定义线程池
- 三、线程池的大小
我们之前写的代码都是,用到线程的时候再创建,用完之后线程也就消失了,实际上这是不对的,它会浪费计算机的内存和运算资源。
线程池的出现就是为了解决这个问题,线程池在收到任务后,会派出一个空闲线程进行处理,处理完毕后,该线程又会回到线程池;如果没处理完时又来了新的任务,那么它就再派出一个线程进行处理,但最后,所有的线程都会再次回到线程池,没有产生任何浪费。
线程池虽然方便,但也是有上限的,这个上限可以由我们自己设置,如果提交任务时池子里没有空闲线程,剩下的线程只能在外等待。
一、线程池的主要核心原理
① 创建一个池子,池子中是空的。
② 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子。下次再提交任务时,不需要创建新的线程,直接复用已有的线程即可。
③ 如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待。
如果要用代码创建线程池,我们需要用到一个新的工具类——Executors。
代码实现如下:
package ExecutorsDemos;
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) 创建一个上限为nThreads的线程池
*/
//1.获取线程池对象
ExecutorService pool1 = Executors.newCachedThreadPool();
//2.提交任务
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
//3.销毁线程池
//pool1.shutdown();
//4.创建一个有上限的线程池
ExecutorService pool2 = Executors.newFixedThreadPool(3);
}
}
二、自定义线程池
线程池创建的构造方法 ThreadPoolExecutor() 最多可达7个参数。如果将线程池比作经营饭店,那么这七个参数对应下面七种要素。
自定义线程池的运行情况和你提交的任务数量有关,假设有一个自定义线程池,核心线程有3个,临时线程有3个,现在根据不同任务数量进行处理。
1.提交3个任务,线程池只需动用3个核心线程分别执行即可。
2.提交5个任务时,此时前3个任务分别被3个核心线程包揽,但是剩下两个任务并没有核心线程可以支持,这时候线程池却不创建临时线程来干活,因此后2个任务只能排队等待。
3.提交8个任务时,此时前3个任务被3个核心线程包揽,然后剩下3个任务去队列里排队,把长度为3的队列也占满了,此时还有2个任务没有去处,这个时候线程池才会创建2个临时线程来处理任务七和任务八。
由这个案例可以知道,先提交的任务不一定优先执行。
4.提交10个任务时,前3个任务被3个核心线程包揽,后3个任务被放入队伍排队,再3个任务被临时线程包揽,此时还剩1个任务,此时这个任务没有线程可以处理,只能触发任务拒绝策略。
Java 中默认的策略就是:丢弃任务并抛出异常,其他三个策略了解一下就行。
自定义线程池的构造方法代码实现如下所示:
package ExecutorsDemos;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorDemo1 {
/*
ThreadPoolExecutor
参数一:核心线程数量
不能小于0
参数二:最大线程数
不能小于等于0,最大数量>=核心线程数量
参数三:空闲线程最大存活时间
不能小于0
参数四:时间单位
用TimeUnit指定
参数五:任务队列
不能为null
参数六:创建线程工厂
不能为null
参数七:任务的拒绝策略
不能为null
*/
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3, //核心线程数量,不能小于0
6, //最大线程数,不能小于0,最大数量 >= 核心线程数
60, //空闲线程最大存活时间
TimeUnit.SECONDS, //时间单位,这里是一个常量"秒"
new ArrayBlockingQueue<>(3), //阻塞队列,长度为3
Executors.defaultThreadFactory(), //创建线程工厂
new ThreadPoolExecutor.AbortPolicy() //任务的拒绝策略,它是一个内部类
);
}
}
三、线程池的大小
线程池多大合适呢?这个我们一般有专门的公式。
一个4核8线程的CPU,最大并行数是8。