Java攻略
2021-12-13 17:03:47 0 举报
AI智能生成
Java攻略
作者其他创作
大纲/内容
集合
Collection
List(有序,可重复)
ArrayList
底层是数组,1.5倍扩容,查询和更新快(通过数组下标),增删慢
LinkedList
底层是是双向链表,不需要扩容,增加和删除快,查询和更新慢
Vector
两倍扩容,线程安全,底层和ArrayList一样是数组,不过代码中加入了大量的synchronise关键字,导致性能低下
Stack类,栈结构,继承Vector,因为父类大量synchronise同步关键字的原因被弃用
Set(无序,不重复)
底层实现是对应的Map
底层实现是对应的Map
HashSet
无序,采用HashMap的key来存储元素
LinkedHashSet
HashSet + LinkedList 的结构,保留了插入时的顺序
TreeSet
红黑树结构,可以有序,可以使用自定义比较器或自然排序来排序
DeQueue(双向队列)
两组API,一种报异常,一种返回值
两组API,一种报异常,一种返回值
LinkedList
双向链表,可以存null值,可以用作先进先出的队列使用,跟List接口下的是同一个类
ArrayDeque
可扩容的数组,不可以存null值,可用作先进先出的队列,也可用作先进后出栈的实现
PriorityQueue(堆)
优先队列,根据某一个规定的优先级排列
Map
HashMap
线程不安全
线程不安全
1.7数组+链表
扩容(resize)
rehash
rehash
扩容:创建一个新的数组,长度是原来的两倍
rehash(长度不同,hash规则不同):重新计算hash值,将原数据重新插入到新数组中
头插法:扩容时会有死循环的可能,新值被认为更有几率被查询
1.8数组+链表+红黑树
扩容(resize)
rehash
rehash
扩容:创建一个新的数组,长度是原来的两倍
rehash(长度不同,hash规则不同):重新计算hash值,将原数据重新插入到新数组中
尾插法:不会产生死循环,但多线程条件下,会存在put的值被覆盖的可能
参数
默认初始化容量(数组长度)16
2的整数次幂,为了实现数组的均匀分布
默认负载因子:0.75
树形化阈值(1.8以后):8
即当链表的长度大于8的时候,会将链表转为红黑树,优化查询效率
树形化最小容量(1.8以后):64
HashMap数组的容量大于等于64时,将链表转化成红黑树
equals&hashCode
为了保证相同的对象返回相同的hash值,不同的对象返回不同的hash值
ConcurrentHashMap
线程安全,效率高
线程安全,效率高
快速失败(fast-fail):当使用迭代器遍历时,如果数组长度发生变化,会抛出异常
1.7版本(segment分段锁)
结构不变:数组+链表
segmen继承于ReentrantLock,理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发
每个segment(段)都可以被当作一个HashMap
使用volatile修饰了entry,保证了线程间的可见性,防止指令重排序
将数组分为多个(segment)段,每个段包含几个entryList,使用时对相应的segment上锁,不会影响其他的segment
get方法不需要加锁,因为加了volatile关键字,保证了每次读到的都是最新的数据
链表限制了查询的速度,如果链表很长,效率也不是很高
1.8版本(synchronise锁+CAS)
采用CAS + synchronized 来保证并发安全性
用Node替代HashEntry,但作用不变
把值和next采用了volatile去修饰,保证了可见性
引入红黑树,当链表长度大于一定值的时候(默认8)会转换成红黑树
put的时候首先尝试CAS写入,失败则自旋
判断是否需要扩容,是否需要转换成红黑树
如果都不满足,使用sychronized锁
判断是否需要扩容,是否需要转换成红黑树
如果都不满足,使用sychronized锁
HashTable
继承Dictionay
线程安全,效率低
继承Dictionay
线程安全,效率低
对数据的操作都会上锁,效率低不用的根本原因
不允许键或值为null,因为安全失败机制,使用空值时,无法判断对应的key是不存在还是为null
安全失败(safe-fail):使用迭代器读取数据时,数组长度变化时,不会报异常,这会使读到的值不一定是最新的值
迭代器也为安全失败的
初始容量为11,扩容为二倍原数组大小+1
synchronizedMap
(同步Map)
(同步Map)
SynchronizedMap内部维护了一个普通对象Map,还有排斥锁mutex
当创建处SynchronizedMap的时候,所有对它的操作都是上了锁的
多线程
线程池
参数
核心线程数大小
总线程数大小
线程池核心线程数最大值
非核心线程存活时间
时间单位
线程空闲存活时间单位
阻塞队列
存放任务的阻塞队列
ArrayBlockingQueue(有界队列)是一个用数组实现的有界阻塞队列,按FIFO
LinkedBlockingQueue(可设置容量队列)基于链表结构的阻塞队列,按FIFO排序
DelayQueue(延迟队列)是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序
PriorityBlockingQueue(优先级队列)是具有优先级的无界阻塞队列
SynchronousQueue(同步队列)一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene
线程工厂
用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题。
拒绝策略
AbortPolicy(抛出一个异常),默认
DiscardPolicy(丢弃这个任务)
DiscardOldestPolicy(丢弃队列里最老的任务,并将这个任务提交给线程池)
CallerRunsPolicy(交给调用线程池所在的线程进行处理)
线程池执行流程
提交任务,核心线程是否已满
交给核心线程处理
核心线程已满,则看阻塞队列是否已满
进入阻塞队列
线程池是否已满
创建非核心线程执行任务
采用拒绝策略
异常处理
使用try-catch捕获
通过Future对象的get方法接收抛出的异常
分类
newFixedThreadPool (固定数目线程的线程池)
newCachedThreadPool(可缓存线程的线程池)
newSingleThreadExecutor(单线程的线程池)
newScheduledThreadPool(定时及周期执行的线程池)
创建线程的方式
继承Thread类,重写run方法
实现Runable接口,重写run方法
实现 Callable 接口,重写call方法
线程状态
NEW
初始状态,还没调用start方法
RUNNABEL
就绪状态
运行中状态
BLOCKED
阻塞状态,处于这个状态的线程需要等待其他线程释放锁或者等待进入 synchronized
WAITING
表示等待状态,处于该状态的线程需要等待其他线程对其进行通知或中断等操作,进而进入下一个状态
TIME_WAITING
超时等待状态。可以在一定的时间自行返回
TERMINATED
终止状态,当前线程执行完毕
JVM
运行时数据区
JMM
堆(线程共享)
大多数对象存放的区域,ps:也有特殊的,逃逸分析模式下的栈上分配
新生代:1
Eden区:8
新对象(小)
survivor1区:1
survivor2区:1
老年代:2
新对象(大)
元空间(线程共享)
类元信息
静态属性
字段、方法、常量
Java虚拟机栈(线程私有)
栈帧
局部变量表
存放方法参数、局部变量
大小确定的连续存储空间
操作数栈
桶式结构栈
方法执行过程中会有很多操作指令入栈出栈
动态链接
常量池中对当前方法的引用
方法返回地址:方法返回的代码现场
异常
StackOverflow:线程请求的深度大于栈允许的深度
OOM:虚拟机栈会动态扩容,如果请求不到相应的空间,则会抛出OOM异常
本地方法栈(线程私有)
本地方法栈为Native方法提供服务
程序计数器(线程私有)
存放指令的偏移量和行号指示器等现场信息
内存分配
指针碰撞,内存规整(基于压缩整理的垃圾收集器使用)
所有用过的内存在一边,空闲的内存在另外一边,中间放着一个指针作为分界点的指示器,分配内存就仅仅是把指针向空闲那边挪动一段与对象大小相等的距离
空闲列表,内存不规整(基于清除算法的垃圾收集器使用)
虚拟机维护了一个列表,记录上哪些内存块是可用的,再分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的内容
垃圾回收
可以被回收的对象
GCRoot的对象
虚拟机栈引用的对象
方法区中静态变量引用的对象
方法区中常量引用的对象
本地方法栈中引用的对象(Native对象)
引用计数法
会产生循环引用,弃用
垃圾回收算法
标记-清除(Mark-Sweep)
产生内存碎片
标记-复制(Mark-Copy)
内存使用率打折
标记-整理(Mark-Compact)
整理过程效率不高
分代回收策略
新生代
标记-复制
Serial,单线程
ParNew,并行的多线程收集器
Parallel Scavenge,并行的多线程收集器
老年代
标记-整理
Serial Old,单线程
Parallel Old,并行多线程收集器
标记-清除
CMS,并发标记清除,采用三色标记法,采用并行和并发收集
致力于最短时间的STW
致力于最短时间的STW
浮动垃圾
CPU敏感
内存碎片
对象消失的问题
增量更新解决
标记-整理
G1(可预测的停顿时间)
Eden新生代(YoungGC)
Survivor区
Old老年代(MixedGC)
Humongous大对象
同样会产生三色标记法出现的问题
浮动垃圾
对象消失问题
原始快照解决
类加载机制
类加载过程
加载(Load)
读取并转化为JVM需要的数据结构,初步校验cafebabe魔法数、常量池、文件长度、是否有父类等,然后创建对应类的实例
读取转化,初步校验
读取转化,初步校验
链接(Link)
验证:详细校验,final是否合规等
准备:(分配内存设默认值)为静态变量分配内存,并设定默认值
解析:(解析确保引用正确)解析类和方法,确保类与类之间的相互引用的正确性,完成内存结构布局
初始化(Init)
初始化(Init)阶段:(赋值)执行类构造器中的< clinit>方法,需要跟实例构造器方法<init>区分,如果赋值运算是通过其他类的静态方法来完成的,那么会马上解析另外一个类,在虚拟机栈中执行完毕后通过返回值进行赋值。
双亲委派模型(非强制)
加载器结构
Bootstrap,加载最核心的Java类,如Object,String等,非Java实现
Platform ClassLoader,平台类加载器,加载一些扩展的系统类
Application ClassLoader 应用类加载器,加载自定义的类
加载流程
向上询问是否已加载
向下尝试是否可加载
自定义类加载器
自定义类加载器优点
隔离加载类:在某些框架内进行中间件与应用模块的隔离,把类加载到不同的环境。比如,阿里内某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包
修改类加载方式:除了Bootstrap外,其他的加载并非一定要引入,或者根据实际情况在某个时间点进行按需进行动态加载
防止源码泄露:Java代码容易编译和篡改,可以进行编译加密。那么类加载器也需要自定义,还原加密的字节码
自定义类加载器流程
继承ClassLoader
重写findClass()方法
调用defineClass()方法
框架
Spring
SpringMVC
IOC(控制反转)
AOP面向切面
JDK动态代理
有接口
cgLib动态代理
没有接口
SpringBoot
0 条评论
下一页