java基础-池化

池,也是经常见到的一个编程方式了,线程池、数据库连接池、对象池。
它的作用比较简单,就是避免频繁的回收和创建对象的。可能大家觉得创建对象很容易,new一下就可以,但是别后jvm要干的事可多了:加载类、分配内存、对象的内存结构初始化(对象头和引用)、对象数据初始化。这是简单对象创建的过程效率还可以接受,但是这样的对象如果数量实在太多也是无法接受的,所以就用对象池。而有一些对象创建的时候更加复杂,典型的就是线程对象,它要在底层支撑更多东西,如计数器、虚拟机方法栈、本地方法栈的分配,甚至是堆上分配一大块内存给该线程分配对象使用等,它的创建和回收更加复杂所以也会影响效率,所以就用线程池来管理。还有一些对象创建的时候需要外部资源特别是IO操作的,比如数据库连接、Socket连接等,这种创建和回收消耗的时间非常可观,有大量外部连接的创建而不用池的都是耍流氓,所以用连接池。

对象池
不太常用,也很少被问到,因为一般对象池都是因为数量庞大而需要池化管理,而对象池要是维护不好,要么效率有问题,要么就是容易内存泄露。见过的常用到的就是netty的byteBuf的对象池,而这个池初期的时候就会有内存泄露问题,也是在我们刚开始要用netty的时候,和领导杠了一段时间说最新版本bug已经修复了,后来做了一些实验和源码截图还有靠谱的文章才说服领导用对象池。

数据库连接池
数据库连接如果不用池化管理肯定是不成的,所以商用软件都会用一个数据库连接池管理数据库连接,这个没啥多说的,就是看哪个适合就用哪个,设置具体参数就OK。

线程池
这个本应该在并发一小节来写,但是这个其实主要考的就是池化的信息,也是大多出池的概念化体现,所以单独拿出来说说。其实本篇也主要就是说线程池。
前面说了为啥用线程池,线程对象的特殊性也使得线程池几乎是必考内容。
线程池的七大参数
核心线程数,保有的最小线程数,线程数不够的不会回收,超过这个数量的超时就会回收
最大线程数,保有的最大线程数,线程池管理的线程数的最大值,但是其实执行的线程总数可能会出这个值,例如丢弃策略是超出后就立马执行。
空闲时间,线程空闲时间过长就要回收,直到剩余核心线程数量的。
时间单位,空闲时间的事件问题。
等待任务队列,一个阻塞队列,当没有空闲可用的线程的时候存放任务使用。
线程创建工厂,创建线程对象的工厂,一般只是用来给创建线程命名使用。
丢弃策略执行器,当执行任务数量超过等待队列上限也超过最大线程数的时候使用该策略决定新加入的任务处理方式。

线程池的运行过程
1.初始化完线程池对象后其实仅仅是设置了一些参数而已。
2.放入任务,若线程数小于core数量,则创建新线程执行。
3.若线程池内线程对象数量大于core数量,并没有空闲线程,并且没有达到max数,则放入等待队列。
4.若等待队列满了,并且线程数没有超过max,则继续创建线程。
5.若队列满了,达到max线程数,则根据丢弃策略处理新来的任务。
6.线程空闲下来了,则任何获取任务的操作都会触发检测空闲线程并进行回收,回收空闲超时的线程直到池中线程数到达core数量

线程池的抛弃策略
线程池实现了四个抛弃策略
1.报告异常,若参数不传抛弃策略那默认就是这个
2.直接执行,这个就是执行线程数可能会大于max
3.丢弃,丢弃策略有两个实现,一个是丢弃新加入的任务,另一个是找到队列里面最老的进行丢弃。

阻塞队列
一般用于多线程的生产者消费者队列,主要是因为它的实现都是线程安全的,内部都是使用锁来进行控制线程并发。
具体实现有:
ArrayBlockingQueue
LinkedBlockingQueue
DelayQueue-特殊实现是进入队列的元素,在延长一段时间后才可以被访问到
PriorityBlockingQueue-优先级队列,出队列的顺序是有优先级的。
SynchronousQueue-内部仅有一个元素,入队列的时候若已经有元素则进行阻塞,直到元素被提取。
BlockingDeque、LinkedBlockingDeque双端队列

posted @ 2021-03-04 18:08  Q-JayLee  阅读(397)  评论(0)    收藏  举报