自己整理的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
Exception
Error
Servlet生命周期
request
out
pageContext
session
application
config
page
内置对象
JSP
JVM提供的最轻量级的同步机制,指令:ACC_VOLATILE
用于修饰变量(不包括局部变量)
可见性:线程A操作变量,强制刷新到主存并设置其他线程内存失效必须从主存获取最新值
有序性:内存屏障,保证屏障上/下都可以指令重排但是屏障内的指令块不能被重排
无法保证原子性:由于有count++操作,该操作是非原子操作
作用:保证变量对所有线程的可见性,以及线程执行的有序性,但不能保证原子性
volatile关键字
修饰方法
修饰代码块
原子性
可见性
有序性
锁优化(锁膨胀)
Synchronized
计数值、双向链表、CAS+自旋
构造函数
AbstractQueuedSynchronizer(AQS)
lock.lock()
tryAcquire
addWaiter
acquireQueued
未获取锁,等待获取锁的实现
lock.unlock()
ReentrantLock
Lock(I)
公平锁/非公平锁
可重入锁
独享锁/共享锁
互斥锁/读写锁
乐观锁/悲观锁
分段锁
偏向锁/轻量级锁/重量级锁
自旋锁
java锁
实例化bean对象
设置对象属性
检查Aware相关接口并设置相关依赖
BeanPostProcessor前置处理
检查是否是InitializingBean以决定是否调用afterPropertiesSet方法
检查是否配置有自定义的init-method
BeanPostPorcessor后置处理
注册必要的Destruction相关回调接口
使用(进行业务逻辑)
是否实现DisposableBean接口
是否配置有自定义的destory方法
spring bean生命周期
将请求发给DispatcherServlet
DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller
DispatcherServlet再把请求提交到对应的Controller
Controller进行业务逻辑处理后,返回一个ModelAndView
Dispatcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象
视图对象负责渲染返回给客户端
spring mvc运行流程
byName和byType
由Spring提供
默认通过byType方式注入
@Autowired
由J2EE提供
默认通过byName方式注入
@Resource
@Autowired和@Resource注解区别
循环依赖发生原因: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的实例化、初始化全部完成
发生循环依赖时的执行流程
spring bean三级缓存解决循环依赖
Spring
解耦
异步
削峰
为什么使用消息队列?
系统可用性降低(万一MQ挂了可能直接导致系统不可用)
系统复杂度提高(怎么保证消息不被重复消费?怎么保证消息不会丢失?)
数据一致性问题(MQ往往是异步处理,那么异步处理消息过程中如果某个服务挂了导致数据没有入库会导致数据不一致)
使用消息队列有什么缺点?
broker1(节点)
partition1(一部分数据)
broker2(节点)
partition2(一部分数据)
broker ...
partition ...
topic存放原理
partition生成多个replica副本
所有replica选举一个leader,其他为follower,由leader负责把数据同步到所有follower
消费者只能读写leader(保证数据一致性)
kafka均匀的将一个partition的所有replica分布在不同节点上,提高系统容错性
HA机制(replica副本机制,leader-follower选举)
如果leader节点宕机,kafka会从follower中重新选举一个leader出来,后面继续读写新的leader
生产者只写leader,leader将数据落到磁盘
follower主动从leader节点pull数据,同步完成后发送ack通知leader数据同步已完成
leader接收到所有follower的ack完成,返回写成功的消息给生产者
写数据
前提:一个消息已经被所有follower都同步并返回ack成功
消费者只会从leader读数据
消费(读数据)
Kafka高可用(分布式消息队列)
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:一旦写入失败,则无限重试
消息可靠性传输(保证消息不会丢失)通过配置解决
针对消息队列缺点的解决方案
消息队列(Kafka)
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
断点续传
slave不会过期key,只会等待master过期key
master过期key之后,会模拟一条del命令发给slave
过期key处理
主从架构(保证高并发)
RDB
AOF
持久化的两种方式(RDB和AOF)
集群监控
消息通知
故障转移
配置中心
主要功能
至少需要3个实例来保证健壮性
不保证数据零丢失,只能保证redis集群的高可用
核心知识
异步复制导致的数据丢失
脑裂导致的数据丢失
解决方案
redis哨兵主备切换的数据丢失问题
sdown -- 主观宕机
odown -- 客观宕机
sdown和odown转换机制
基于pub/sub(发布/订阅)系统实现的
channel:__sentinel__:hello
每个哨兵都订阅了channel,已达到互相发现的目的
chennel的消息内容包括了对master节点的监控配置,哨兵之间会互相交换,互相进行监控配置的同步
哨兵之间自动发现(互相发现)
跟master断开连接的时长
slave的优先级
复制的offset
runid
slave --> master选举算法
哨兵模式(保证高可用)
缓存雪崩
缓存穿透
缓存击穿
缓存击穿/穿透/雪崩
数据类型支持
持久化支持
复制和高可用性
性能差异
redis和memcached区别
Redis
原子性(Atomicity)
一致性(Consisitency)
脏读
不可重复读
幻读
事务隔离级别需要解决的问题
读未提交
读提交
可重复读
串行化
事务隔离级别
隔离性(Isolation)
持久性(Durability)
数据库事务
抽象
继承
封装
多态
面向对象
Byte
byte
Integer
int
Character
char
Long
long
Float
float
Double
double
Boolean
boolean
Short
short
Java基本数据类型
String
StringBuffer
String与StringBuffer
Vector
普通方式
内部类方式
Arrays.asList
Collections.nCopies
初始化
初始数组长度10,这里是设置数组elementData长度为10,但是size变量还是0
普通插入--add(\"yl\"):size++,放入元素
判断长度充足:ensureCapacityInternal(size+1)
长度不足,调用扩大函数grow(int minCapacity)进行扩容
扩容长度计算(扩容为原容量的3/2即1.5倍):int newCapacity = oldCapacity + (oldCapacity >> 1);
插入时扩容(动态扩容)
插入
ArrayList
LinkedList
AbstractList
List(I)
HashSet
TreeSet
LinkedHashSet
AbstractSet
Set(I)
Deque
DelayQueue
SynchronousQueue
PriorityBlockingQueue
LinkedBlockingQueue
ArrayBlockingQueue
BlockingQueue
PriorityQueue
ConcurrentLinkedQueue
AbstractQueue
Queue
Collection(I)
HashMap
TreeMap
LinkedHashMap
HashTable
AbstractMap
Map(I)
Iterator(I)
Collections
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
扩容元素拆分
1.hash扰动,获取到新的hash值,然后开始进入put方法执行
2.判断tab是否为空或者长度为0,是的话就进行扩容
3.根据hash值计算下标,判断下标是否有数据,没有则直接插入,否则需要覆盖
4.判断tab[i]是否为树节点,否的话进行链表插入,是的话进行树节点插入
6.所有元素处理完毕,判断长度是否超过阈值,超过则扩容
put方法
1.hash扰动,获取到新的hash值
2.计算下标 tab[(n - 1) & hash]
3.确定了桶数组和下标位置,就进行链表/红黑树遍历查找操作
get方法
HashMap原理
集合框架
BIO
NIO
AIO
对比
NIO/BIO/AIO
线程的三大特征
继承Thread类
实现Runnable接口
实现Callable接口
基于线程池的方式
线程实现/创建方式
Executors.newCachedThreadPool
Executors.newFixedThreadPool
Executors.newScheduledThreadPool
Executors.newSingleThreadPool
线程池
新建(new)
就绪(runnable)
运行(running)
等待阻塞
同步阻塞
其他阻塞
阻塞(blocked)
正常结束
异常结束
调用stop
死亡(dead)
线程生命周期(状态)
sleep()
wait()
并发
数据结构:散列表
Entry(弱引用的实现):发生GC会被垃圾回收
数据元素存储方式:哈希散列--斐波那契散列法0.618,时间复杂度O(1)
发生哈希碰撞不会像HashMap那样转为链表/红黑树,而是使用拉链法(下标+1向后寻址)进行存储
数据结构:散列表的数据结构(数组+hash值计算下标)
初始数组长度16
常量:private static final int HASH_INCREMENT = 0x61c88647;
计算hashcode:nextHashCode.getAndAdd(HASH_INCREMENT);
最后将hashcode & (len - 1)得到最终数组下标
散列算法(斐波那契散列)
待插入的下标,位置为空,直接插入
待插入的下标,位置不为空,key相同,直接更新
待插入的下标,位置不为空,key不相同,拉链法寻址
待插入的下标,位置不为空,key不相同,碰到过期key,探测式清理过期key(弱引用GC)再插入
设置元素:new ThreadLocal<>().set(\"yl\");分四种情况
判断sz >= threshold(阈值),其中threshold = len * 2 / 3,也就是数组填充的元素大于len*2/3,就需要扩容
调用rehash()进行扩容,重新计算元素位置
扩容
key直接定位到,没有哈希冲突,直接获取
有哈希冲突,key不同,拉链寻址+1获取
有哈希冲突,key不同,拉链寻址,遇到了GC清理元素,通过探测式清理(清理完把后续位置前移),再继续获取元素
获取元素:new ThreadLocal<>().get();分三种情况
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
解决办法
Entry弱引用导致内存泄露问题
ThreadLocal
Java
收藏
收藏
0 条评论
回复 删除
下一页