什么是线程池?

线程池是Java线程的一种使用模式,通过池的思想对线程的创建和使用进行统一的管理。

为什么要用线程池?

Java线程是稀缺资源,频繁的创建和销毁会对CPU带来一定的开销,线程过多也会带来调度开销,不易维护和管理,进而影响缓存局部性和整体的性能。使用线程池可以对线程进行复用,避免了在处理短时间任务时创建与销毁线程的代价,还能防止过分调度,线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。

线程池原理

线程池使用了池化技术,核心思想是把稀缺资源放到一个池子中进行统一管理,需要使用时从池中获取,使用结束后再放回到池中,供其他人使用。
了解了线程池的核心思想之后,我们一起来看看Java如何实现线程池。

1. 线程池的创建

Java创建线程池的方式主要有两种,一种是通过JDK1.5推出的Excutors类的静态方法进行创建,通过该类可以创建三种不同类型的线程池

(1)创建固定大小的线程池

java线程池下再开线程 java 线程 线程池_高并发编程


(2)创建单个线程的线程池

java线程池下再开线程 java 线程 线程池_任务队列_02


(3)创建无限大小的线程池

java线程池下再开线程 java 线程 线程池_线程池_03


使用Excutors静态方法创建线程池的优点是方便简单,而缺点也很明显,那就是不容易对线程池进行扩展。

另一种创建线程池的方式是直接使用ThreadPoolExecutor类创建

java线程池下再开线程 java 线程 线程池_任务队列_04


ThreadPoolExecutor构造器具备如下几个核心参数:

  • corePoolSize:线程池的核心线程数量。
  • maximumPooSize:线程池的最大线程数量。
  • keepAliveTime 和 unit: 线程池空闲存活时间和时间单位。
  • workQueue:存放任务的阻塞队列。
  • handler:当任务队列和最大线程数都满了之后所执行的拒绝策略。
线程池的工作流程

通过调用线程池的execute(Runnable r) 传递一个任务到线程池中,线程池接收到任务后,首先判断当前线程池的核心线程数是否已经达到最大值,如果未达到则创建一个线程执行该任务,反之判断当前任务队列是否已满,如果任务队列未满的话将该任务加入到任务队列中,等待后期的任务调度。否则再去判断当前线程池的总线程数是否达到最大值,如果未达到,创建一个线程执行任务。而如果核心线程数、任务队列、总线程数都达到饱和后、此时线程池无法在接收新的任务,将会执行拒绝策略,拒绝该任务进入到线程池中。

java线程池下再开线程 java 线程 线程池_java线程池下再开线程_05


线程池中定义了物种线程状态,这些状态和线程的执行密切相关。

java线程池下再开线程 java 线程 线程池_线程池_06


- RUNNING 运行状态,可以接收用户提交的任务和执行任务队列里的人物

- SHUTDOWN 调用shutdown()方法后,不再接收新任务,但需要将队列里的任务全部执行完。

- STOP 调用了shutdownNow()方法后,不再接收新任务,同时抛弃任务队列里的所有任务。

- TIDYING 在调用shutdown() 或 shutdownNow()方法后会尝试更新为当前状态,处于当前状态下线程池中执行的任务为空。

- TERMINATED 终止状态,当执行terminated()后会更新=为该状态。

状态转换路程如下

java线程池下再开线程 java 线程 线程池_Java_07

线程池的使用
ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 20, 30,
         TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 20; i++) {
            pool.execute(new Runnable(){
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在运行");
                }
            });
            
        }