什么是线程池?为什么要有线程池?
怎么实现一个最简单的线程池?(附代码)
线程池大小怎么确定?
如今随着CPU的更新迭代,随着用户需求的不断增长,并发编程的重要性日益增高,由于进程的创建和销毁开销太大了,多线程技术应运而生,作为一种轻量级的进程,线程的创建和销毁的开销比进程要小很多,因此多线程更适合于并发编程。但是由于用户需求的更加增长,线程的创建和销毁所需要开销同样不可忽视,为了解决这个问题,协程和线程池技术被开发出来。协程是更轻量级的线程,但是协程并未进入Java标准库,在这里就不多介绍。
线程池:将线程提前创建好,放到池子里,需要的时候直接从池子里获取,用完了之后又放回池子里。
线程池的优点:
最大的好处就是减少每次启动、销毁线程的损耗
- 降低资源消耗:重复利用已创建的线程来降低线程创建和销毁造成的开销。
- 提高响应速度:当任务到达时,可以不需要线程等待创建就可以立即执行。
标准库中的线程池:
使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
返回值类型为 ExecutorService
通过 ExecutorService.submit 可以注册一个任务到线程池中.
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
Executors 创建线程池的几种方式
newFixedThreadPool: 创建固定线程数的线程池
newCachedThreadPool: 创建线程数目动态增长的线程池.
newSingleThreadExecutor: 创建只包含单个线程的线程池.
newScheduledThreadPool: 设定延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
Executors 本质上是 ThreadPoolExecutor 类的封装.
实现一个最简单的线程池:
实现之前我们需要了解,线程池的核心操作是什么,需要其他什么操作?
1.注册任务交给线程池
2.使用一个优先级队列存放线程
3.线程不断取出任务并且立即执行
4.指定一下线程池中最大线程数量
import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque; public class ThreadDemo { public static void main(String[] args) { MyThreadPool pool = new MyThreadPool(10); for (int i = 10; i < 1000; i++) { int n = i; //变量捕获 pool.submit(new Runnable() { @Override public void run() { System.out.println("hello" + n); } }); } } }; class MyThreadPool { //创建一个保存任务的队列 private BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>(); //n表示线程数量 public MyThreadPool(int n) { for (int i = 0; i < n; i++) { //创建线程 ,取出任务立即执行 Thread t1 = new Thread(() -> { while (true) { try { Runnable runnable = queue.take(); runnable.run(); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); t1.start(); } } //注册任务交给线程池 public void submit(Runnable runnable) { try { queue.put(runnable); } catch (InterruptedException e) { throw new RuntimeException(e); } } }
线程池大小怎么确定?
没有一个确定的说法,推荐根据需求来~~
CPU密集型:这种任务主要需要CPU的调度,可以将线程数量设置为CPU核心数N+1
IO密集型:这种情况下CPU调度需求不高,所以CPU可以有更多时间来完成其他线程的任务,因此可以多配一些线程,推荐为2N