ArrayList扩容机制
ArrayList底层是个数组结构的存储容器,初始长度是10,可以通过构造器或者根据size()方法来指定初始容量,当数组调用add()方法存储10次之后,就没有多余的容量来存储了,就会触发ArrayList的扩容机制。因为底层是数组,所以扩容方法跟数组底层是一样的,简单讲就三步:
第一步:创建新数组,然后数组长度会比原来大1.5倍 (10->15);
第二步:调用Arrays的copyOf方法,将原数组的内容复制到新数组中;
第三部:完成扩容之后把新添加的数据添加到新数组中;
HashMap底层结构
jdk1.8之前底层基于数组+链表实现。jdk1.8之后底层基于数组+链表+红黑树实现。
数组中每个元素的位置我们叫“桶”,调用put方法往HashMap里放值时,HashMap会根据key的hash值取模来计算桶的位置,然后看该位置上是否有链表存在,如果没有链表就创建一个链表指向这个桶的位置,若已经有链表存在,就采用尾插法把节点插入到链表;当链表长度达到8并且数组长度小于64时,链表不会转换为红黑树,会先触发数组扩容,;当数组长度大于64并且链表长度大于8时,链表会转换为红黑树;
HashMap扩容机制
HashMap中的元素个数超过数组长度乘以负载因子时,就会重新分配一个更大的数组,并将原来的元素重新计算哈希值并插入到新的数组中。不同版本的Java实现了不同的扩容机制。
在JDK1.7中,HashMap的初始容量默认是16,负载因子默认是0.75,阈值默认是12(16*0.75)。当HashMap中的元素个数超过阈值时,就会触发扩容。新的容量是原来的2倍,新的阈值也是原来的2倍。在扩容过程中,HashMap会遍历原来的数组中的每个链表,并将链表中的每个节点重新计算哈希值,找到新数组中对应的位置,以头插法插入到新链表中。这样做可能会导致链表反转和多线程环境下的死循环问题。
在JDK1.8中,HashMap在第一次调用put方法时才会初始化数组,而不是在创建对象时就初始化。HashMap在初始化或扩容时,会根据指定或默认的容量找到不小于该容量的2的幂次方,并将其赋值给阈值。然后在第一次调用put方法时,会将阈值赋值给数组长度,并让新的阈值等于数组长度乘以负载因子。在扩容过程中,HashMap不需要重新计算节点的哈希值,而是根据哈希值最高位判断节点在新数组中的位置,要么在原位置,要么在原长度加上原位置处。在扩容过程中,HashMap会正序遍历原来的数组,并保持链表中节点的相对顺序不变。如果某个链表中的节点数超过8个,并且数组长度大于等于64,则会将链表转化为红黑树,提高查找效率。
总的来说,HashMap的扩容机制是为了平衡性能和内存使用。当元素个数超过一定阈值时,HashMap会通过重新分配一个更大的数组和重新计算元素的哈希值来进行扩容,以提高性能和效率。
池化技术
基于资源复用的思想,将一些“昂贵的”,“费时的”资源预先分配到一个特定的容器中,从而优化应用性能和资源开销。常见的池化技术包括:数据库连接池,线程池,redis连接池,内存池和对象池。
线程池
避免重复的创建和销毁线程。先启动若干线程,并让这些线程处于休眠状态,当需要开辟一个线程去完成工作时,就会唤醒某一个线程去完成工作,工作完成之后再处于休眠状态;
线程池参数:java中提供的有多种线程池,每个线程池都有自己的优势,但是底层都是调用的同一个
创建线程池的构造方法;
1、corePoolSize(核心线程数):线程池中保持活动状态的最小线程数,即使是空闲状态也不会被回收
2、maximumPoolSize(最大线程数):线程池中允许存在的最大线程数,包括核心线程数和非核心线程数。
3、keepAliveTime(线程空闲时间):当线程池中的线程数超过核心线程数时,空闲线程等待新任务的最长时间,超过这个时间将被回收销毁。
4、TimeUnit(时间单位):设置keepAliveTime的时间单位,如秒、毫秒等。
5、workQueue(任务队列):用于存放等待执行的任务的阻塞队列,可以选择不同类型的队列,如ArrayBlockingQueue、LinkedBlockingQueue等。
6、threadFactory(线程工厂):用于创建线程的工厂类,可以自定义线程的名称、优先级等属性。
7、rejectedExecutionHandler(任务拒绝策略):当线程池无法接收新任务时,执行的策略,如抛出异常、丢弃任务或者调用主线程来执行任务。
线程状态
新建状态 -> 就绪状态:当线程创建完成后,它自动进入就绪状态。
就绪状态 -> 运行状态:当CPU调度器选择该线程时,线程进入运行状态。
运行状态 -> 阻塞状态:当线程需要等待某个资源或事件时,它进入阻塞状态。
阻塞状态 -> 就绪状态:当阻塞的线程所等待的资源或事件变得可用时,它重新进入就绪状态。
就绪状态 -> 运行状态:当CPU调度器再次选择该线程时,线程重新进入运行状态。
运行状态 -> 终止状态:当线程执行完毕或被中止时,它进入终止状态。