初稿

编程入门 行业动态 更新时间:2024-10-28 06:32:05
常见知识点总结:

一、Java基础

1.1、HashMap相关

基本数据结构(1.7和1.8)

数组+链表/数组+链表+红黑树,转换时机

红黑树的定义(为什么不能是其他树)

基本定义:是一种只含有红黑结点并能自平衡的二叉查找树(左旋、右旋、变色),再插入和删除破坏树的平衡结构时,会进行自旋到平衡

基本性质

性质1:每个节点要么是黑色,要么是红色。

性质2:根节点是黑色。

性质3:每个叶子节点(NIL)是黑色。

性质4:每个红色结点的两个子结点一定都是黑色。

性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

性质5.1:如果一个结点存在黑子结点,那么该结点肯定有两个子结点

扩容机制(2倍,为什么)

无参构造初始大小16,加载因子0.75,2的幂次,超过长度*负载因子时会2倍扩容,好处:计算索引位置时,
避免了一些Hash冲突,如果当前位置有元素(替换或生成链表,取决于key的值),当链表长度大于8

hash碰撞定义如何解决 不同的值有不同的Hashcode
可参考文章

引申(同类,并发,实现区别,包括ConcurrentHashMap,HashTable)

多线程,扩容的时候线程不安全 HashTable,全部synchronized,
1.8之前用了可重入锁,
1.8之后用了无锁,解决的ConcurrentHashMap ConcurrentHashMap

在JDK1.7版本中,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成,
Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,
而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样

JDK1.8的实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,
并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,
虽然在JDK1.8中还能看到Segment的数据结构,但是已经简化了属性

put 方法执行机制

先是

  public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}

再是

    /*** Implements Map.put and related methods** @param hash hash for key* @param key the key* @param value the value to put* @param onlyIfAbsent if true, don't change existing value* @param evict if false, the table is in creation mode.* @return previous value, or null if none*/final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}

1.2、内存模型

基本内存模型和数据共享区(和JDK版本有关系)

1.8之前

1.8之后

关于类加载过程

堆内存细分?

1.3、GC

基本算法,可参考链接

1、吞吐量:即单位时间内的处理能力。 2、最大暂停时间:因执行GC而暂停执行程序所需的时间。
3、堆的使用效率:鱼与熊掌不可兼得,堆使用效率和吞吐量、最大暂停时间是不可能同时满足的。即可用的堆越大,GC运行越快;相反,想要利用有限的堆,GC花费的时间就越长。
4、访问的局部性:在存储器的层级构造中,我们知道越是高速存取的存储器容量会越小(具体可以参看我写的存储器那篇文章)。由于程序的局部性原理,将经常用到的数据放在堆中较近的位置,可以提高程序的运行效率。

基本算法包括:标记清除算法,复制算法,标记压缩算法

1.4、线程池

1.4.1、基本参数及其含义

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {}

一、corePoolSize 线程池核心线程大小

线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会 被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。

二、maximumPoolSize 线程池最大线程数量

一个任务被提交到线程池后,首先会缓存到工作队列(后面会介绍)中,如果工作队列满了,则会创建一个新线程,然后从工作队列中的取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize来指定。

三、keepAliveTime 空闲线程存活时间

一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定

四、unit 空间线程存活时间单位

keepAliveTime的计量单位

五、workQueue 工作队列

新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

①ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。
当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,
则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。

②LinkedBlockingQuene: 基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。
由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,
而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。

③SynchronousQuene:一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,
而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。

④PriorityBlockingQueue: 具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

六、threadFactory 线程工厂

创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

七、handler 拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。
这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:

①CallerRunsPolicy:该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务

②AbortPolicy:该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。

③DiscardPolicy:该策略下,直接丢弃任务,什么都不做。

④DiscardOldestPolicy:该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

1.4.2、实际操作应注意的

1.核心线程满了,接下来进队列,队列也满了,创建新线程,直到达到最大线程数,之后再超出,会进入拒绝rejectedExecution

1.4.3、JUC下基本线程池

五种基本线程池使用场景

1.4.4、Spring线程池使用

1.5、并发,锁

synchronized重量级锁

二、Spring/Springboot,基础框架系列知识点

2.1、Bean 生命周期

2.2、SpringBean执行机制

2.3、JPA+QueryDsl相关

2.4、Mybatis相关(1,2级缓存)

本地缓存和二级缓存,范围不同,本地缓存是sqlSession级别,二级缓存基于nameSpace,多表的时候,出现脏数据

三、微服务相关知识点

3.1、微服务基本架构方式

3.1.1、 AlibabaCloud(nacos+sentinel) / Dubbo

3.1.2、 SpringCloud(eureka+feign+hystrix+apollo+zuul+gateway+ribbon)

相关组件源码相关组件使用及场景链路追踪

eureka 的相关注册发现源码
feign源码实现
hystrix断路器配置
ribbon负载均衡策略

几种均衡策略包括

RandomRule 随机策略 随机选择server

RoundRobinRule 轮询策略 按照顺序选择server(ribbon默认策略)

RetryRule 重试策略 在一个配置时间段内,当选择server不成功,则一直尝试选择一个可用的server

BestAvailableRule 最低并发策略 逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server

AvailabilityFilteringRule 可用过滤策略 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值)

ResponseTimeWeightedRule 响应时间加权重策略 根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间

ZoneAvoidanceRule 区域权重策略 综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server

apollo架构和使用

四、MQ

四大MQ,包括Kafka,ActiveMQ,RabbitMQ,RocketMQ

4.1、Kafka

4.1、ActiveMQ

4.1、RabbitMQ

4.1、RocketMQ

五、搜索引擎/Nosql

包括ES,Solr,Redis,MongoDB

5.1、Redis相关

5.1.1、 基本使用场景

分布式锁实现、优缺点,和注意事项缓存MQ排行榜

六、数据库

6.1、基础知识

索引类型和方法SQL优化(执行计划+索引+SQL优化)

七、数据结构和算法

更多推荐

初稿

本文发布于:2023-05-20 18:53:15,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/154772.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:初稿

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!