吓我一跳?看了线程和线程池的对比,才知道池化技术到底有多牛( 二 )


内存池的优点:

  • 减少内存碎片的产生 , 这个优点可以从创建内存池的过程中看出 , 当我们在创建内存池的时候 , 分配的都是一块块比较规整的内存块 , 减少内存碎片的产生 。
  • 提高了内存的使用频率 。 这个可以从分配内存和释放内存的过程中看出 。 每次的分配和释放并不是去调用系统提供的函数或操作符去操作实际的内存 , 而是在复用内存池中的内存 。
内存池的缺点:会造成内存的浪费 , 因为要使用内存池需要在一开始分配一大块闲置的内存 , 而这些内存不一定全部被用到 。
3.数据库连接池数据库连接池的基本思想是在系统初始化的时候将数据库连接作为对象存储在内存中 , 当用户需要访问数据库的时候 , 并非建立一个新的连接 , 而是从连接池中取出一个已建立的空闲连接对象 。 在使用完毕后 , 用户也不是将连接关闭 , 而是将连接放回到连接池中 , 以供下一个请求访问使用 , 而这些连接的建立、断开都是由连接池自身来管理的 。
同时 , 还可以设置连接池的参数来控制连接池中的初始连接数、连接的上下限数和每个连接的最大使用次数、最大空闲时间等 。 当然 , 也可以通过连接池自身的管理机制来监视连接的数量、使用情况等 。
吓我一跳?看了线程和线程池的对比,才知道池化技术到底有多牛文章插图
4.HttpClient 连接池HttpClient 我们经常用来进行 HTTP 服务访问 。 我们的项目中会有一个获取任务执行状态的功能使用 HttpClient , 一秒钟请求一次 , 经常会出现 Conection Reset 异常 。 经过分析发现 , 问题是出在 HttpClient 的每次请求都会新建一个连接 , 当创建连接的频率比关闭连接的频率大的时候 , 就会导致系统中产生大量处于 TIME_CLOSED 状态的连接 , 这个时候使用连接池复用连接就能解决这个问题 。
实战:线程 VS 线程池本文我们使用之前文章介绍的统计方法《6种快速统计代码执行时间的方法 , 真香!(史上最全)》 , 来测试一下线程和线程池执行的时间差距有多大 , 测试代码如下:
import java.util.concurrent.LinkedBlockingDeque;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;/** * 线程池 vs 线程 性能对比 */public class ThreadPoolPerformance { // 最大执行次数public static final int maxCount = 1000;public static void main(String[] args) throws InterruptedException {// 线程测试代码ThreadPerformanceTest();// 线程池测试代码ThreadPoolPerformanceTest();}/*** 线程池性能测试*/private static void ThreadPoolPerformanceTest() throws InterruptedException {// 开始时间long stime = System.currentTimeMillis();// 业务代码ThreadPoolExecutor tp = new ThreadPoolExecutor(10, 10, 0,TimeUnit.SECONDS, new LinkedBlockingDeque<>());for (int i = 0; i < maxCount; i++) {tp.execute(new PerformanceRunnable());}tp.shutdown();tp.awaitTermination(1, TimeUnit.SECONDS);// 等待线程池执行完成// 结束时间long etime = System.currentTimeMillis();// 计算执行时间System.out.printf("线程池执行时长:%d 毫秒.", (etime - stime));System.out.println();}/*** 线程性能测试*/private static void ThreadPerformanceTest() {// 开始时间long stime = System.currentTimeMillis();// 执行业务代码for (int i = 0; i < maxCount; i++) {Thread td = new Thread(new PerformanceRunnable());td.start();try {td.join(); // 确保线程执行完成} catch (InterruptedException e) {e.printStackTrace();}}// 结束时间long etime = System.currentTimeMillis();// 计算执行时间System.out.printf("线程执行时长:%d 毫秒.", (etime - stime));System.out.println();} // 业务执行类static class PerformanceRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < maxCount; i++) {long num = i * i + i;}}}}以上程序的执行结果如下图所示:
吓我一跳?看了线程和线程池的对比,才知道池化技术到底有多牛文章插图
为了防止执行的先后顺序影响测试结果 , 下面我将线程池和线程调用方法打个颠倒 , 执行结果如下图所示:
吓我一跳?看了线程和线程池的对比,才知道池化技术到底有多牛文章插图
总结从线程和线程池的测试结果来看 , 当我们使用池化技术时 , 程序的性能可以提升 10 倍 。 此测试结果并不代表池化技术的性能量化结果 , 因为测试结果受执行方法和循环次数的影响 , 但巨大的性能差异足以说明池化技术的优势所在 。
无独有偶 , 阿里巴巴的《Java开发手册》中也强制规定「线程资源必须通过线程池提供 , 不允许在应用中自行显式创建线程」规定如下:
吓我一跳?看了线程和线程池的对比,才知道池化技术到底有多牛文章插图