1-认识线程池
什么是线程池?
- 线程池就是一个可以复用线程的技术。
不使用线程池的问题
- 比方说淘宝,不使用线程池,现在有一亿个线程同时进来,CPU就爆了。
- 用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。
1.1-什么是并发/并行?
进程是什么?
- 正在运行的程序(软件)就是一个独立的进程。
- 线程是属于进程的,一个进程中可以同时运行很多个线程。
- 进程中的多个线程其实是并发和并发执行的。
并发的含义
- 进程中的线程是CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。
2-创建线程池
- JDK5.0起提供了代表线程池的接口:ExecutorService。
如何创建线程池对象?
- 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。
- 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。
2.1-方式一:通过ThreadPoolExecutor创建线程池
public static void main(String[] args) {
//目标:创建线程池对象,并使用。
//1.使用线程池的实现类:ThreadPoolExecutor来创建线程池声明七个参数来创建线程池对象。
ExecutorService pool = new ThreadPoolExecutor(
3,
5,
1000,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
2.2-方式二:通过Executors创建线程池
- Executors是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。
注意事项
- 这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。
Executors使用可能存在的陷阱
- 大型并发系统环境中使用Executors如果不注意可能会出现系统风险。
3-线程池任务处理
3.1-处理Runnable任务
3.1.1-ExecutorService的常用方法
我们可以看到总共就三个线程一直在跑,说明做到了线程的复用。
3.1.2-线程池的关闭
//3.关闭线程池对象:一般不关闭,而是让调用者来关闭。
pool.shutdown(); //线程池 shutdown()方法,不再接受新的任务,但是会处理完已经提交的任务。
//会等待所有任务执行完毕后再关闭线程池。
pool.shutdownNow();//立即关闭线程池 shutdownNow()方法,不再接受新的任务,并且打断正在执行的任务。
3.1.3-线程池的注意事项
什么时候开始创建临时线程?
- 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
什么时候会拒绝新任务?
- 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。
任务拒绝策略
代码实例
3.2-处理Callable任务
这就是线程池如何处理Callable任务,并得到任务执行后返回的结果的办法。
- 使用ExecutorService的方法
- Future<T>submit(Callable<T>command)