自己整理的java技术栈相关内容
2024-04-26 10:35:47 0 举报
AI智能生成
我的Java技术栈内容包括Java编程语言的基础知识、面向对象编程原理、Java核心API的使用、Java多线程编程、Java网络编程、Java高级特性以及J2EE框架。我拥有丰富的Java项目实战经验,并掌握了Spring、Spring MVC、Hibernate和MyBatis等流行框架的使用。此外,我还熟练掌握JavaEE规范,了解Servlet、JSP、JDBC等方面的内容。在文件类型方面,我精通XML和JSON格式的文件处理。在修饰语方面,我熟悉Java的关键字、常量、变量和运算符,以及控制流程结构和数组等数据结构的使用。总之,我具备扎实的Java技术功底和项目开发经验,能够在团队合作中发挥积极作用。
作者其他创作
大纲/内容
final,finally,finalize
final
finally
finalize
Exception
Exception
Error
JSP
Servlet生命周期
内置对象
request
out
pageContext
session
application
config
page
java锁
volatile关键字
JVM提供的最轻量级的同步机制,指令:ACC_VOLATILE
用于修饰变量(不包括局部变量)
作用:保证变量对所有线程的可见性,以及线程执行的有序性,但不能保证原子性
可见性:线程A操作变量,强制刷新到主存并设置其他线程内存失效必须从主存获取最新值
有序性:内存屏障,保证屏障上/下都可以指令重排但是屏障内的指令块不能被重排
无法保证原子性:由于有count++操作,该操作是非原子操作
公平锁/非公平锁
Synchronized
修饰方法
修饰代码块
原子性
可见性
有序性
锁优化(锁膨胀)
Lock(I)
ReentrantLock
计数值、双向链表、CAS+自旋
构造函数
AbstractQueuedSynchronizer(AQS)
lock.lock()
未获取锁,等待获取锁的实现
tryAcquire
addWaiter
acquireQueued
lock.unlock()
可重入锁
独享锁/共享锁
互斥锁/读写锁
乐观锁/悲观锁
分段锁
偏向锁/轻量级锁/重量级锁
自旋锁
Spring
spring bean生命周期
实例化bean对象
设置对象属性
检查Aware相关接口并设置相关依赖
BeanPostProcessor前置处理
检查是否是InitializingBean以决定是否调用afterPropertiesSet方法
检查是否配置有自定义的init-method
BeanPostPorcessor后置处理
注册必要的Destruction相关回调接口
使用(进行业务逻辑)
是否实现DisposableBean接口
是否配置有自定义的destory方法
spring mvc运行流程
将请求发给DispatcherServlet
DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller
DispatcherServlet再把请求提交到对应的Controller
Controller进行业务逻辑处理后,返回一个ModelAndView
Dispatcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象
视图对象负责渲染返回给客户端
@Autowired和@Resource注解区别
byName和byType
@Autowired
由Spring提供
默认通过byType方式注入
@Resource
由J2EE提供
默认通过byName方式注入
spring bean三级缓存解决循环依赖
循环依赖发生原因:A依赖B,B也依赖A
通过@Autowired注解注入的bean可以自动解决循环依赖,通过构造器注入的bean无法自动解决
发生循环依赖时的执行流程
遍历所有需要创建的Bean,此时第一次遍历到A,发现A在缓存没有,开始执行正常的构建流程
A初始构建完成,判断A是否单例并且bean未创建完毕,则把A的beanFactory放入到三级缓存
A开始处理@Autowired注解,发现依赖了B,则尝试从缓存获取B,未获取到则开始正常创建B的流程
B初始构建完成,判断B是否单例并且bean未创建完毕,把B的beanFactory放入到三级缓存
B开始处理@Autowired注解,发现依赖了A,依次尝试从一/二/三级缓存获取A,此时在三级缓存能获取到A的beanFactory,通过beanFactory.getObject()方法获取到A,接着把A放入到二级缓存并清除三级缓存,此时B填充A属性成功
继续执行B的bean初始化和实例化,最终得到B的完全体并执行addSington()方法,将B放入一级缓存,清除二三级缓存
接着进行A填充B的属性,A调用beanFactory.getBean()获取到B,此时可以从一级缓存取到B,继续执行后续初始化和实例化,最后得到完全状态的A
A执行addSington()方法,将A放入一级缓存,清除二三级缓存
第二次遍历到B,发现一级缓存有B,取出并返回,此时A和B的实例化、初始化全部完成
消息队列(Kafka)
为什么使用消息队列?
解耦
异步
削峰
使用消息队列有什么缺点?
系统可用性降低(万一MQ挂了可能直接导致系统不可用)
系统复杂度提高(怎么保证消息不被重复消费?怎么保证消息不会丢失?)
数据一致性问题(MQ往往是异步处理,那么异步处理消息过程中如果某个服务挂了导致数据没有入库会导致数据不一致)
针对消息队列缺点的解决方案
Kafka高可用(分布式消息队列)
topic存放原理
partition1(一部分数据)
broker1(节点)
partition2(一部分数据)
broker2(节点)
partition ...
broker ...
HA机制(replica副本机制,leader-follower选举)
partition生成多个replica副本
所有replica选举一个leader,其他为follower,由leader负责把数据同步到所有follower
消费者只能读写leader(保证数据一致性)
kafka均匀的将一个partition的所有replica分布在不同节点上,提高系统容错性
如果leader节点宕机,kafka会从follower中重新选举一个leader出来,后面继续读写新的leader
写数据
生产者只写leader,leader将数据落到磁盘
follower主动从leader节点pull数据,同步完成后发送ack通知leader数据同步已完成
leader接收到所有follower的ack完成,返回写成功的消息给生产者
消费(读数据)
前提:一个消息已经被所有follower都同步并返回ack成功
消费者只会从leader读数据
保证消息消费的幂等性(消息不被重复消费)
Kafka的offset(消息序号)概念
开发层面解决幂等性
入库前先查询,再确定是insert还是update
如果是写入redis,redis的set能够保证天然幂等性
数据库唯一键约束保证
消息可靠性传输(保证消息不会丢失)通过配置解决
topic设置replication.factor:必须大于1,要求每个partition至少有2个副本
Kafka服务端设置min.insync.replicas:必须大于1,要求一个leader至少感知到一个follower跟自己保持联系
producer端设置acks=all:要求follower都写入成功返回ack给leader才算数据写入成功
producer端设置retries=MAX:一旦写入失败,则无限重试
Redis
数据类型
String(简单KV存储,get/set)
List(有序列表,可以实现简单队列,lpush/rpop)
Set(无序集合,可以用集合交集/并集等,实现共同好友业务,sadd)
Sorted Set(有序集合,做排行榜,zadd)
Hash(存储无内嵌其他对象的结构化数据,hset/hget)
其他(BitMap、HyperLogLog、Stream)
过期策略(定期删除+惰性删除)
定期删除
惰性删除
内存淘汰机制(内存不足时写入数据)
noeviction
allkeys-lru
allkeys-random
volatile-lru
volatile-random
volatile-ttl
主从架构(保证高并发)
核心机制
异步复制
一个master可配置多个slave
slave之间可以互连
slave复制
原理
启动一个slave node时,slave会发送一个PSYNC命令给master node
如果slave node是初次连接master node,会触发一次full resynchronization全量复制
master随即启动一个后台线程,生成一份RDB文件,并将client后续发出的所有写命令缓存到内存
RDB文件生成完毕,由master发给slave,slave会先写入本地磁盘,然后再从本地磁盘写入到内存
接着master将内存的后续所有写命令发给slave,slave进行同步
断点续传
master在内存中维护了一个backlog
master和slave都保存了一个replica offset和master run id,offset就在backlog里
如果master和slave网络连接断开,slave会从上次replica offset开始继续复制
如果没有找到offset,就会执行一次resynchronization
过期key处理
slave不会过期key,只会等待master过期key
master过期key之后,会模拟一条del命令发给slave
持久化的两种方式(RDB和AOF)
RDB
AOF
哨兵模式(保证高可用)
主要功能
集群监控
消息通知
故障转移
配置中心
核心知识
至少需要3个实例来保证健壮性
不保证数据零丢失,只能保证redis集群的高可用
redis哨兵主备切换的数据丢失问题
异步复制导致的数据丢失
脑裂导致的数据丢失
解决方案
sdown和odown转换机制
sdown -- 主观宕机
odown -- 客观宕机
哨兵之间自动发现(互相发现)
基于pub/sub(发布/订阅)系统实现的
channel:__sentinel__:hello
每个哨兵都订阅了channel,已达到互相发现的目的
chennel的消息内容包括了对master节点的监控配置,哨兵之间会互相交换,互相进行监控配置的同步
slave --> master选举算法
跟master断开连接的时长
slave的优先级
复制的offset
runid
缓存击穿/穿透/雪崩
缓存雪崩
缓存穿透
缓存击穿
redis和memcached区别
数据类型支持
持久化支持
复制和高可用性
性能差异
数据库事务
原子性(Atomicity)
一致性(Consisitency)
隔离性(Isolation)
事务隔离级别需要解决的问题
脏读
不可重复读
幻读
事务隔离级别
读未提交
读提交
可重复读
串行化
持久性(Durability)
面向对象
抽象
继承
封装
多态
Java基本数据类型
byte
Byte
int
Integer
char
Character
long
Long
float
Float
double
Double
boolean
Boolean
short
Short
String与StringBuffer
String
StringBuffer
集合框架
Collection(I)
List(I)
AbstractList
Vector
ArrayList
初始化
普通方式
内部类方式
Arrays.asList
Collections.nCopies
插入
初始数组长度10,这里是设置数组elementData长度为10,但是size变量还是0
普通插入--add("yl"):size++,放入元素
指定位置插入--add(2, "yl"):rangeCheckForAdd方法index和size进行比较,if (index>size || index <0)抛异常IndexOutOfBoundsException
插入时扩容(动态扩容)
判断长度充足:ensureCapacityInternal(size+1)
长度不足,调用扩大函数grow(int minCapacity)进行扩容
扩容长度计算(扩容为原容量的3/2即1.5倍):int newCapacity = oldCapacity + (oldCapacity >> 1);
最后调用Arrays.copyOf(elementData, newCapacity)将原数组中的数据拷贝到新数组中,拷贝底层用的System.arraycopy
LinkedList
Set(I)
AbstractSet
HashSet
TreeSet
LinkedHashSet
Queue
Deque
BlockingQueue
DelayQueue
SynchronousQueue
PriorityBlockingQueue
LinkedBlockingQueue
ArrayBlockingQueue
AbstractQueue
PriorityQueue
ConcurrentLinkedQueue
Map(I)
AbstractMap
HashMap
TreeMap
LinkedHashMap
HashTable
Iterator(I)
Collections
HashMap原理
HashTable和HashMap区别
HashTable
HashMap
hashcode(32位 int类型)
数据结构(数组+链表/红黑树)
扰动函数
目的:让元素数据更加均衡的散列,减少哈希碰撞,增加随机性
设计:hashcode是32位int类型数值,而hashmap初始容量为16,所以hash值不能直接作为数组下标使用
扰动函数运算逻辑
1.获取hashcode值
2.hashcode右移16位:h << 16
3.右移后的值与原hashcode进行异或运算
4.最后拿数组长度(length-1)和运算结果进行与运算,得到最终索引位置
初始容量:数组=16 链表=8(链表超过8转红黑树,红黑树少于6转链表)
负载因子0.75
扩容元素拆分
原哈希值 & 扩容新增长度(16),进行与运算
如果运算结果为0,则下标位置不变,否则新的位置=原来的位置+16
put方法
1.hash扰动,获取到新的hash值,然后开始进入put方法执行
2.判断tab是否为空或者长度为0,是的话就进行扩容
3.根据hash值计算下标,判断下标是否有数据,没有则直接插入,否则需要覆盖
4.判断tab[i]是否为树节点,否的话进行链表插入,是的话进行树节点插入
5.进行链表插入节点时,判断链表长度是否大于等于8,是的话调用treeifyBin(tab, hash)将链表转为红黑树
6.所有元素处理完毕,判断长度是否超过阈值,超过则扩容
7.treeifyBin(tab, hash)方法不止判断链表长度大于等于8,还要判断存放key值的数组长度是否小于64,如果小于64则先扩容,把元素散列到扩容后的位置,减少链表长度
get方法
1.hash扰动,获取到新的hash值
2.计算下标 tab[(n - 1) & hash]
3.确定了桶数组和下标位置,就进行链表/红黑树遍历查找操作
NIO/BIO/AIO
BIO
NIO
AIO
对比
并发
线程的三大特征
原子性
可见性
有序性
线程实现/创建方式
继承Thread类
实现Runnable接口
实现Callable接口
基于线程池的方式
线程池
Executors.newCachedThreadPool
Executors.newFixedThreadPool
Executors.newScheduledThreadPool
Executors.newSingleThreadPool
线程生命周期(状态)
新建(new)
就绪(runnable)
运行(running)
阻塞(blocked)
等待阻塞
同步阻塞
其他阻塞
死亡(dead)
正常结束
异常结束
调用stop
sleep(),wait()
sleep()
wait()
ThreadLocal
数据结构:散列表的数据结构(数组+hash值计算下标)
数据结构:散列表
Entry(弱引用的实现):发生GC会被垃圾回收
数据元素存储方式:哈希散列--斐波那契散列法0.618,时间复杂度O(1)
发生哈希碰撞不会像HashMap那样转为链表/红黑树,而是使用拉链法(下标+1向后寻址)进行存储
散列算法(斐波那契散列)
初始数组长度16
常量:private static final int HASH_INCREMENT = 0x61c88647;
计算hashcode:nextHashCode.getAndAdd(HASH_INCREMENT);
最后将hashcode & (len - 1)得到最终数组下标
设置元素:new ThreadLocal<>().set("yl");
分四种情况
分四种情况
待插入的下标,位置为空,直接插入
待插入的下标,位置不为空,key相同,直接更新
待插入的下标,位置不为空,key不相同,拉链法寻址
待插入的下标,位置不为空,key不相同,碰到过期key,探测式清理过期key(弱引用GC)再插入
扩容
启发式清理:cleanSomeSlots(i, sz)方法,把过期元素清理掉,再判断空间
判断sz >= threshold(阈值),其中threshold = len * 2 / 3,也就是数组填充的元素大于len*2/3,就需要扩容
调用rehash()进行扩容,重新计算元素位置
获取元素:new ThreadLocal<>().get();
分三种情况
分三种情况
key直接定位到,没有哈希冲突,直接获取
有哈希冲突,key不同,拉链寻址+1获取
有哈希冲突,key不同,拉链寻址,遇到了GC清理元素,通过探测式清理(清理完把后续位置前移),再继续获取元素
Entry弱引用导致内存泄露问题
继承关系
Thread类中存在静态内部类:ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap中存在静态内部类Entry
Entry继承了WeakReference<ThreadLocal<?>>
问题复现
问题分析
a置为null后,由于key为弱引用,Entry的key(ThreadLocal)会被回收
a置为null前,调用set进行了value设值,key被回收就会导致value成为不可达的强引用对象,value会随着Entry被回收而回收
如果使用了线程池,线程池内的线程不会被销毁,会导致ThreadLocalMap不回收,从而发生内存泄漏
解决办法
ThreadLocal使用结束后,必须要使用.remove()方法,将其value置为null,避免产生内存泄露
JDK1.8的ThreadLocal进行了处理,set、get、remove方法都会清除ThreadLocalMap中所有key为null的value
收藏
收藏
0 条评论
下一页