站点图标 度崩网-几度崩溃

什么是线程池?

什么是线程池?

线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。

为什么要使用线程池?

因为 Java 中创建一个线程,需要调用操作系统内核的 API,操作系统要为线程分配一系列的资源,成本很高,所以线程是一个重量级的对象,应该避免频繁创建和销毁。
使用线程池就能很好地避免频繁创建和销毁。

线程池是一种生产者——消费者模式

先看下一个简单的 Java 线程池的代码

package constxiong.concurrency.a010;import java.util.ArrayList;import java.util.List;import java.util.concurrent.BlockingQueue;/** * 简单的线程池 * @author ConstXiong */public class ThreadPool {//阻塞队列实现生产者-消费者BlockingQueue<Runnable> taskQueue;//工作线程集合List<Thread> threads = new ArrayList<Thread>();//线程池的构造方法ThreadPool(int poolSize, BlockingQueue<Runnable> taskQueue) {this.taskQueue = taskQueue;//启动线程池对应 size 的工作线程for (int i = 0; i <poolSize; i++) {Thread t = new Thread(() -> {while (true) {Runnable task;try {task = taskQueue.take();//获取任务队列中的下一个任务task.run();} catch (InterruptedException e) {e.printStackTrace();}}});t.start();threads.add(t);}}//提交执行任务void execute(Runnable task) {try {//把任务方法放到任务队列taskQueue.put(task);} catch (InterruptedException e) {e.printStackTrace();}}}

线程池的使用测试

package constxiong.concurrency.a010;import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;/** * 测试线程池的使用 * @author ConstXiong */public class TestThreadPool {public static void main(String[] args) {// 创建有界阻塞任务队列BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>(10);// 创建 3个 工作线程的线程池ThreadPool tp = new ThreadPool(3, taskQueue);//提交 10 个任务for (int i = 1; i <= 10; i++) {final int j = i;tp.execute(() -> {System.out.println("执行任务" + j);});}}}

打印结果

执行任务1执行任务2执行任务3执行任务6执行任务5执行任务4执行任务8执行任务7执行任务10执行任务9

这个线程池的代码中

线程池的原理就是这么简单,但是 JDK 中的线程池的功能,要远比这个强大的多。

JDK 中线程池的使用

JDK 中提供的最核心的线程池工具类 ThreadPoolExecutor,在 JDK 1.8 中这个类最复杂的构造方法有 7 个参数。

ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)

JDK 的并发工具包里还有一个静态线程池工厂类 Executors,可以方便地创建线程池,但是由于 Executors 创建的线程池内部很多地方用到了无界任务队列,在高并发场景下,无界任务队列会接收过多的任务对象,导致 JVM 抛出OutOfMemoryError,整个 JVM 服务崩溃,影响严重。所以很多公司已经不建议使用 Executors 去创建线程。

Executors 的简介

虽然不建议使用,作为对 JDK 的学习,还是简单介绍一下.

  1. newFixedThreadPool:创建定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程
  2. newCachedThreadPool:创建可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
  3. newScheduledThreadPool:创建定长线程池,可执行周期性的任务
  4. newSingleThreadExecutor:创建单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行
  5. newSingleThreadScheduledExecutor:创建单线程可执行周期性任务的线程池
  6. newWorkStealingPool:任务可窃取线程池,不保证执行顺序,当有空闲线程时会从其他任务队列窃取任务执行,适合任务耗时差异较大。