线程池 基本原理
做个Java开发的小伙伴,或多或少的接触过线程池,但是为什么Java要提供线程池这个工具类呢?相信大家都听说过,线程对象比较重,重是什么意思?大家应该也接触过其他池化技术,比如数据库连接池,那这些池化技术和线程池的原理是否一样呢?我们先来明确一下,重到底是什么意思?一个普通的Java对象,它的创建过程无非就是加载类,在堆内存中分配空间,进行初始化,经过这三步Java对象就创建完成了,而线程对象的创建,除了需要经过这三步,还需要操作系统的配合,真正线程的创建是交给操作系统的,而操作系统创建线程就会用到其他资源(如果学过操作系统,至少应该知道操作系统会维护一个TCB),正是由于操作系统申请了其他资源,所以导致线程对象比普通的Java对象重。而对于这些比较重的对象,但是我们又需要频繁使用,一般我们会选择将它们缓存起来,用的时候申请并使用,不用的时候释放并让它回到缓存中。这就是池化技术,如果在程序中,需要频繁使用比较重的对象,那么一般都会利用池化技术提升性能。一般的池化技术基本上就是提前创建一堆对象,用的时候,拿去用,不用的时候,放回来。但是线程池无法这样设计,虽然从设计上,看上去可行,需要使用线程时,申请一个线程,使用完后,将这个线程放回池中。但是线程有个很致命的问题,线程创建之初,他所需要执行的流程就固定了,无法修改。所以一般的池化技术的设计思路就不可取了,于是Java对于线程池的设计,采用的生产者-消费者模型 生产者是使用线程池的我们,消费者就是线程池中的线程。

使用线程池的我们,通过线程池提供的API,将我们需要执行的任务加入到阻塞队列中,而每个线程都执行着固定的流程,即从阻塞队列中取任务,调用它们的run()方法。这就是Java对于线程池的设计。
不过在日常开发中,创建线程池的时候,推荐使用有界的阻塞队列,但是Java提供的Excutors.newxxxx()这些方法,传入的阻塞队列都是无界的即没有大小限制,很容易导致OOM(内存不够)
