java后端笔记

news2024/12/25 13:43:51

写在前面

节选自:
黑马程序员:新版Java面试专题视频教程:https://www.bilibili.com/video/BV1yT411H7YK?p=1
javaguide:https://javaguide.cn/home.html

集合

ConcurrentHashMap

JDK1.7:分段数组+链表,用ReentrantLock锁的是HashEntry数组,粒度大(锁住一段数组)
JDK1.8:和HashMap一致,数组+链表/红黑树,用CAS+Sync锁保证线程安全。空节点就用CAS,链表/红黑树就用Sync锁住首节点,粒度小,并发高。
在这里插入图片描述
在这里插入图片描述

Redis

缓存穿透

查询不存在的数据,也没写入缓存,导致每次查询控制都直接访问数据库
1、缓存空值;
2、布隆过滤器:用多个hash code为1标识值存在,查询时都为1,表示有可能存在,其中一个为0,则一定不存在。

缓存击穿

缓存过期时,刚好有大量访问该key,都会访问数据库
1、缓存未命中,多个线程争抢互斥锁,申请到的才访问DB并加入缓存,否则睡眠后重新尝试获取缓存和锁
2、不设置redis TTL,而是在value写入逻辑过期时间,手动判断过期。如果过期,则获取锁,返回旧值,由子线程查询DB并更新缓存。若获取锁失败,也返回旧值。(其实着中国逻辑,可以理解为缓存是一直存在的)
在这里插入图片描述

缓存雪崩

大量key同时失效;redis宕机
1、设置key的TTL时添加随机值
2、使用集群提高redis可用性
3、给缓存业务添加降级限流策略(nginx、Spring Cloud Gateway)(对多种问题都适用)
4、给业务添加多级缓存(Guava、Caffeine)

双写一致性

先介绍业务,根据业务选择实际策略:强一致?最终一致?
1、先更新数据库再删除缓存:因为删除缓存的速度远快于DB,因此更新完DB后再删除缓存时被中断的可能性小。而先删除缓存再更新数据库,有可能在删除完缓存,在等待DB的时候被中断,导致其他线程用旧的值重新赋上
2、延时双删:删缓存 - 修改数据库 - 子线程延时删缓存。延时需要提供时间给数据库做主从同步,且预留时间给其他读线程把旧值写入缓存,再删除旧值。会有一段时间脏数据,时间难控制。
3、强一致:读写都加锁。读用共享锁,写用排他锁。
4、最终一致(略延迟):1、修改数据库 - MQ - 更新缓存;2、Canal:修改数据库 - Canal监听binlog - 更新缓存,基于mysql主从实现
在这里插入图片描述
在这里插入图片描述

持久化

1、RDB(Redis Database Backup file),所有数据都记录到磁盘,fork子进程共享主进程的内存数据库(页表),写入RDB文件
进程都没办法直接操作物理内存,只能操作虚拟内存,并通过页表进行映射。因此子进程只需要拷贝页表即可共享主进程内存,fork速度快,再写入新的RDB文件替换旧文件。
在备份过程中如果有新的写入,那主进程会备份原始数据,对备份数据进行读写。
在这里插入图片描述

2、AOF(Append Only File),记录所有写命令,默认关闭。AOF文件会比RDB大得多。
AOF重写:多次写的命令,只保留最后一次生效的命令
在这里插入图片描述

数据过期删除策略

1、惰性删除:用到时判断过期才删除,对CPU友好,但可能占用内存
2、定期删除:每隔一段时间检测过期并删除
通常配合使用。如果中间因为策略遗漏了过期key的删除,还有数据淘汰机制兜底。

数据淘汰

缓存过多,内存被占满
默认noevictrion,不删除任何数据,直接报错
还有对allkeys、volatile维度的ttl、random、lru、lfu的删除策略。

allkeys是对全体keys的策略,
volatile是只对设置了TTL的keys的策略,便于保留置顶数据。

ttl是TTL小的先淘汰,
random是随机,
lru Least Recently Used,最近访问时间越久的先淘汰,
lfu Least Frequently Used,最近访问频率低的先淘汰

分布式锁

结合业务:定时任务、抢单、幂等性
Redis的setnx命令,SET if not exist,根据返回值判断设置成功/失败,但控制锁的时长不好控制。太短可能提前释放,太长可能客户端宕机,没有手动释放,要等一段时间。
可以用redisson的看门狗机制,只要不显式设置过期时间,就会触发该机制,后台还是会设置过期时间,并且开启看门狗线程,定期的续期该分布式锁。都是基于lua脚本完成的,可以保证原子性。

redisson分布式锁可重入,在背后做了记录,key是锁的key,field是线程名,value是重入数
在这里插入图片描述

主从数据的锁一致问题

Redis主节点获取锁,还没同步给slave就挂了。
发生概率低,可以用红锁,即在多个Redis实例(n / 2 + 1)上加锁。但性能低,官方也不推荐。
AP思想:高可用redis,CP思想:强一致用zookeeper

集群方案

主从复制:高并发

读写分离,写master,读slave,一般一主多从,无法保证高可用
在这里插入图片描述

数据同步

通过replication id判断是同一个数据集,不一致则RDB全量同步,同时记录下同步期间的命令至repl_baklog,随后也进行同步。
否则增量同步,根据offset去获取repl_baklog中之后的命令数据。
在这里插入图片描述

哨兵模式:Sentinel,高可用

Sentinel集群持续监控主备设备的状态,若发现故障,会提升slave为master并通过Redis客户端。其中 通过ping命令监控。
在这里插入图片描述

主观下线:一个Sentinel认为master下线
客观下线:超过一定数量(quorum)的Sentinel认为该实例主观下线,则该实例客观下线。quorum最好超过Sentinel实例数量一半。
选主规则:
从前往后的顺序判断
1、主从断开时间超过阈值的淘汰,证明丢失数据过多;
2、选优先级高的;
3、选offset值高的;
4、选index大的(实例序号而已,无实际意义)

脑裂

master和sentinel连接出现问题,但和Redis客户端连接正常,sentinel会在slave中重新选主。那么在客户端连接新的master之前,成功发给旧master的信息都会被吞掉,旧master恢复连接变为slave后,会被新master同步并刷为旧数据。
在这里插入图片描述

解决:
1、配置最少slave数为1,否则不让写
2、降低主从节点的同步间隔,减少数据丢失,并尽快识别出没有slave节点

分片集群:海量数据存储、高并发写

多个master,每个master都有slave,master之间通过ping检测健康,客户端能访问任意一台master,都会被转发到正确的master上。
在这里插入图片描述

每个master分配一定区间的哈希槽,根据请求计算hash,转发到对应master上。如果请求提供了有效部分,用有效部分计算hash,否则用key计算。
在这里插入图片描述

华为云的Redis的主备模式,实际上是使用哨兵模式来管理,只是客户不感知哨兵存在。

为什么单线程这么快

1、纯内存操作;
2、单线程避免了上下文切换、线程安全问题;
3、使用IO多路复用模型,非阻塞IO;
4、内置了多种优化后的数据结构

因此性能瓶颈是网络IO而非运行速度,IO多路复用模型就是为了解决这个瓶颈问题的。

linux内存分为用户空间和内核空间,用户进程要访问硬件设备(网卡)时,需要:用户缓冲区 - 内核缓冲区 - 硬件设备 的交互,反之亦然。中间的效率问题涉及到:
1、等待数据就绪;
2、数据拷贝
在这里插入图片描述

三种IO方式:
1、阻塞IO:阻塞等待数据就绪,阻塞等待数据拷贝完成
2、非阻塞IO:非阻塞等待数据,但会不断询问是否就绪,导致CPU空转;阻塞等待数据拷贝完成
3、IO多路复用:一次性获取所有数据已就绪的socket列表,单线程循环地阻塞拷贝已就绪的数据。

监听socket、获取通知的方式:
1、select;2、poll;3、epoll
前两种只会通知用户进程有socket就绪,需要用户进程遍历socket列表,确认哪个就绪。epoll会在通知socket就绪的同时,就把已就绪的socket写入用户空间。
IO多路复用用的就是epoll

Redis网络模型

IO多路复用+事件派发。
每个socket会处理不同的请求,把准备就绪的请求派发给对应的处理器。
6.0之后引入多线程,因为瓶颈是网络IO,所以对涉及网络读写的请求、回复模块使用多线程解析,但具体操作命令还是单个主线程,线程安全。
在这里插入图片描述

mysql

慢查询

定位:
端到端定位:Arthas、Prometheus、Skywalking
mysql定位:开启慢查询日志记录(开关、时间阈值)

分析:
使用EXPLAIN或DESC获取SQL语句的执行计划
EXPLAIN可以查看索引使用情况、是否有回表查询、命令的查询类型(const、eq_ref…)等

索引

帮忙mysql高效索引数据的数据结构,有序的B+树。InnoDB存储引擎的索引结构就是B+树。

B+树

1、B+树更矮胖,故IO次数更少;
2、数据只存在于叶子节点,每次访问次数相近,查询性能更稳定;
3、叶子节点间有双相指针,范围查询更方便;

聚簇索引、非聚簇索引

聚簇索引:Clustered index。将数据存储和索引结构放到一起,索引结构的叶子节点存储了行数据。这种索引必须有,且只有一个。
二级索引、非聚簇索引:Secondary index。将数据和索引分开存储,叶子节点只存放主键,用于与数据关联。这种索引可以存在多个。

回表查询

在二级索引取到主键,再拿主键去聚簇索引取行数据。

覆盖索引

查询时使用了索引,并且需要的列,在该索引中能全部找到。不用回表查询。

并发编程

1、原子性
2、可见性
3、有序性(指令重排只会保证单个线程的最终一致性,不保证多线程)

JMM

Java Memory Model
JAVA自己提供的一套内存模型,能屏蔽操作系统的内存模型实现跨平台。
有主内存作为各线程的共享内存,每个线程有各自的工作内存,备份了共享变量副本,需要通过共享内存相互同步。
在这里插入图片描述

JAVA内存区域和JAVA内存模型的关系?

JAVA内存区域划分了数据的存储区域,例如堆存放对象实例;
JAVA内存模型和并发编程、跨平台相关,规范了线程和主内存间共享变量的使用规范,增强了程序的可移植性。

happens-before原则

为了平衡JMM下的并发问题和编译器、处理器的优化性能,只要不改变程序的执行结果(单线程和正确的多线程),不管怎么优化重排都行,否则禁止重排

CAS

Compare And Swap,在无锁下保证变量操作的原子性,JUC(Java.Util.Concurrent)内、AQS(AbstractQueuedSynchronizer)框架、AtomicXXX类都用到。
在修改共享变量时,对比共享内存的值和自身缓存的值是否一致,一致则合入共享内存,否则拷贝进来 重新操作再对比,直到一致为止,因此也叫自旋锁。
底层是依赖OS的CAS操作。
是乐观锁的思想。

可能有的问题:
1、ABA问题:仅对比值一致,不代表值没有被其他现场修改过,有可能被改为B又改回A了。
解决思路是:变量内加入版本号、时间戳等唯一标识符。AtomicStampedReference的compareAndSet就是检查引用一致和标志是否符合预期。
2、循环时间长:JVM支持暂停,延迟尝试。

volatile

表示这个共享变量是不稳定的

1、可见性:保证了可见性,(多线程的)共享变量被volatile修饰,修改后会立刻从线程内存写到主内存。否则修改后不确定何时写入。
2、可见性:避免了JIT对代码优化导致读共享变量失败,例如子线程while(flag)被优化为while(true)
3、有序性:volatile变量的写操作阻止上方其他写操作到下面,读操作组织下方其他操作到上面
使用场景:1、作为子线程的状态标识;2、单例模式的double check
在这里插入图片描述

sync 重量级锁

本质是monitor,是重量级锁,底层依赖操作系统的Mutex Lock实现,OS实现线程间切换涉及到用户态和内核态的切换、线程上下文切换,因此性能较低。
javap -v xx.class查看字节码信息,用monitorenter和monitorexit框起来,其中monitorexit会有两次,是为了防止线程抛异常导致死锁。

对象锁,会关联Monitor结构,具体是用对象的mark word指向Monitor。
Monitor包含WaitSet、EntryList、Owner。WaitSet是线程等待,EntryList是线程阻塞,Owner是获得锁。Owner释放后,EntryList不是排队,会争抢Owner,非公平锁。
在这里插入图片描述

轻量级锁(自旋锁)

同步代码块不存在竞争、或者线程是交替执行该代码块,会用轻量级锁。
用CAS交换Lock Record开头和mark word,用完了再换回。
支持同线程重入,第二个线程起,Lock Record开头值为null,用于计算重入次数。
如果多线程冲突(CAS失败N次),就升级为重量级锁。
CAS自旋是cpu空转,但轻微的自旋空转,能换取用户态和内核态切换的开销。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

偏向锁

只有一个线程会用到该对象锁。
只有第一次CAS时将线程ID放到mark word,重入时不用再CAS,只要判断现场ID是自己。
在这里插入图片描述

偏向锁 → 轻量级锁 → 重量级锁,是单向的升级过程,不会重新降级。

悲观锁和乐观锁

悲观锁:
用sync关键字或ReentrantLock类等锁住代码块。
通常用于写比较多的情况下(多写场景,竞争激烈),避免频繁失败和重试影响性能,且开销是固定的。

乐观锁:
提交修改时再验证是否被其他线程修改,可以用版本号、CAS等思想

AQS

AbstractQueuedSynchronizer,抽象队列同步器,是构建锁或者其他同步组件的基础框架。
实现有:
ReentrantLock 阻塞式锁、
Semaphore 信号量、
CountDownLatch 倒计时器

内部维护一个FIFO的队列 和 state状态量

线程用CAS的方式修改state,修改成功则获得锁,否则进入队列。
state释放后,可以实现公平锁,也可以实现非公平锁的争抢。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ReentrantLock

主要利用CAS+AQS实现,还支持了其他功能:超时释放、公平锁、多个条件变量等。
在这里插入图片描述

ThreadLocal

内存泄露

1、ThreadLocal是每个Thread线程内部维护了一个ThreadLocalMap的成员变量,Map中的每个元素是一个Entry,key-value形式,key是对ThreadLocal对象的弱引用,value是具体set和get的Obect。
弱引用是如果引用的对象只剩下弱引用,那么GC的时候就会把该对象回收,因此key被回收了,则无法通过key找到value,且Entry中对value是强引用,value将一直不会被回收。
会有这种情况,在new ThreadLocal的时候,并没有绑定到某个变量上,那么这个ThreadLocal(即key)则会只剩下Entry的弱引用。

2、如果线程池中有固定的核心线程数,线程使用了ThreadLocal存储对象,如果在线程任务结束时不手动remove对象,则对象会随着核心现场的存活一直存在。

因此:
1、ThreadLocal需要强引用绑定到变量,通常绑定到静态变量上;
2、用完ThreadLocal线程结束前,手动remove。

Map冲突处理

ThreadLocalMap和HashMap类似,通过hash code取模得到数组的下标idx。如果遇到冲突,HashMap是在该idx上使用链表或红黑树存储,而ThreadLocalMap是使用线性探测法找到合适的idx,直到遇到相同的key进行替换value,或者为空直接存入。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1040260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP 和 UDP哪个更好

传输控制协议 (TCP) 和用户数据报协议 (UDP) 是互联网的基础支柱,支持从网络源到目的地的不同类型的数据传输。TCP更可靠,而UDP优先考虑速度和效率。本文解释了两种协议的工作原理,并详细讨论了…

分享53个Python源码源代码总有一个是你想要的

分享53个Python源码源代码总有一个是你想要的 链接:https://pan.baidu.com/s/1ew3w2_DXlSBrK7Mybx3Ttg?pwd8888 提取码:8888 项目名称 100-Python ControlXiaomiDevices DRF-ADMIN 后台管理系统 FishC-Python3小甲鱼 Flask框架的api项目脚手架 …

LLM-TAP随笔——语言模型训练数据【深度学习】【PyTorch】【LLM】

文章目录 3、语言模型训练数据3.1、词元切分3.2、词元分析算法 3、语言模型训练数据 数据质量对模型影响非常大。 典型数据处理:质量过滤、冗余去除、隐私消除、词元切分等。 训练数据的构建时间、噪音或有害信息情况、数据重复率等因素都对模型性能有较大影响。训…

JavaScript系列从入门到精通系列第九篇:JavaScript中赋值运算符和关系运算符以及Unicode编码介绍

一:赋值运算符 1: 右侧的值可以赋值给左侧的变量。 var a 123; console.log(a);//123 2: var a 10; a a 5; a 5; 上边这两个写法是一样的。 3:- var a 10; a a-5; a - 5; 上边这两个写法是一样的。 4:* …

Cesium 展示——label 从十进制小数转度分秒

文章目录 需求分析 需求 Cesium 显示 坐标点label 从十进制小数转度分秒 分析 写一个转换方法 function decimalToDMS(decimal) {const degree Math.floor(decimal);const minute Math.floor((decimal - degree) * 60);const second ((decimal - degree) * 60 - minute) *…

ChunJun: 自定义插件

序言 Chunjun的版本兼容可能会有问题,在我们了解了自定义插件后,在修改源码以应对不同的场景就会得心应手了,针对Chunjun1.12.Release版本说明cuiyaonan2000163.com 自定义插件整体流程 从数据流的角度来看ChunJun,可以理解为不同数据源的数据流通过对应的ChunJu…

一文了解数据治理全知识体系!

在业界,大家都为如何做好数据治理而感到困惑。数据治理工作一定要先摸清楚数据的家底,规划好路线图,再进行决策。 本文从数据治理的误区、元数据管理、数据质量管理、数据资产管理等4个方面整理出数据治理的一套经验总结,给予数据…

python算法部署(通信篇)

1.dockerflask方式 # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license """ Run a Flask REST API exposing one or more YOLOv5s models """import argparse import io import jsonimport torch from flask import Flask, jsonify, req…

公司新来的实习生问我SpringBoot多个环境的配置方式

这是一篇写给新手的文章,老手可以绕行了。 起因是一个同学让我帮他看个问题,他说有两个环境,一个环境有问题,另一个环境没问题,但是一直找不到原因,假设一个环境是 dev,另一个环境是 test。 于…

2023 版 QQ 机器人运行部署文档

文章目录 1.前置说明2.机器人框架的下载与运行2.1 下载机器人框架2.2 下载可操作框架的静态页面2.3 运行机器人框架 3.登录QQ机器人3.1 前提说明3.2 扫码登录3.3 注意事项 4.后端程序处理消息4.1 下载 stater4.2 stater 基本说明4.3 运行后端程序4.4 测试消息处理4.5 关于扩展消…

ISE_ChipScope Pro的使用

1.ChipScope Pro Core Inserter 使用流程 在之前以及编译好的流水灯实验上进行学习 ChipScope的使用。 一、新建一个ChipScope 核 点击Next,然后在下一个框中选择 Finish,你就会在项目菜单中看到有XX.cdc核文件。 二、对核文件进行设置 右键“Synthesize – XST” …

计算机丢失ac1st16.dll怎么解决?教你简单的修复ac1st16.dl文件

在使用Windows操作系统时,有时会遇到一些DLL文件丢失的问题,其中之一就是AC1ST16.DLL。AC1ST16.DLL是用于AutoCAD相关软件的动态链接库文件,当该文件丢失时,可能会导致软件无法正常运行。本文将详细介绍多种解决AC1ST16.DLL丢失问…

OpenGL超级宝典(第五版)第8章fbo_drawbuffers例子分析

目录 1. 概述 2. 疑难点剖析 2.1 SetupRC函数分析 2.2 multibuffer.vs分析 2.3 RenderScene分析 3. 其它 1. 概述 《OpenGL超级宝典(第五版)》如下: 该书第8版的 fbo_drawbuffers工程展示了如下技术点: 什么是帧缓冲区对象&#xff08…

【Linux】进程间通信方式②——文件共享映射(附图解与代码实现)

我们来简单了解下文件共享映射的定义:通过映射文件,使用映射机制,实现资源共享,完成进程通信 具体是如何实现的呢?跟随着这篇博客,我们来看一看 进程通过文件共享映射实现通信的具体步骤 由某一进程创建映…

【湖科大教书匠】计算机网络随堂笔记第2章(计算机网络物理层)

目录 2.1、物理层的基本概念 2.2、物理层下面的传输媒体 导引型传输媒体 同轴电缆 双绞线 光纤 电力线 非导引型传输媒体 无线电波 微波 红外线 可见光 2.3、传输方式 串行传输和并行传输 同步传输和异步传输 单向通信(单工)、双向交替通信&#xf…

【LeetCode-简单题】110. 平衡二叉树

文章目录 题目方法一:后序递归 题目 方法一:后序递归 递归遍历的同时判断是否是平衡二叉树,如果不是,就置为-1,如果是 就正常做递归求最大深度 参考图解网址 判断平衡二叉树 class Solution {public boolean isBalanc…

誉天在线项目-放大招-Vue3集成RichText富文本客户端组件QuillEditor

背景 开发中我们需要填写图文内容,就是含有图片和文字,html标准组件中是没有的。都是第三方来实现,就需要我们去集成。 有早期的fckEditor、ckEditor等,新的我们使用了vue框架,市场又推出了quillEditor。下面我们就在…

【【萌新的SOC学习之绪论】】

萌新的SOC学习之绪论 Vitis 统一软件平台的前身为 Xilinx SDK,从 Vivado 2019.2 版本开始,Xilinx SDK 开发环境已统一整合 到全功能一体化的 Vitis 中。Vitis 开发平台除了启动方式、软件界面、使用方法与 SDK 开发平台略有区别, 其他操作几…

使用acme.sh申请免费ssl证书(Cloudflare方式API自动验证增加DNS Record到期证书到期自动重新申请)

下载acme.sh curl https://get.acme.sh | sh -s emailmyexample.comcd ~/.acme.sh/获取Cloudflare密钥 Preferences | Cloudflare 登录选择账户详情选择API Token选择创建令牌选择区域DNS模板,并设置编辑写入权限生成并复制令牌备用回到首页概览界面下部获取账号…

【RabbitMQ实战】3分钟在Linux上安装RabbitMQ

本节采用docker安装RabbitMQ。采用的是bitnami的镜像。Bitnami是一个提供各种流行应用的Docker镜像和软件包的公司。采用docker的方式3分钟就可以把我们想安装的程序运行起来,不得不说真的很方便啊,好了,开搞。使用前提:Linux虚拟…