Spring AOP 默认使用两种方式来天生代理对象:
- JDK 动态代理:如果目标类实现了接口,Spring 会使用 JDK 动态代理来创建代理对象。这是默认的代理机制。
- CGLIB 代理:如果目标类没有实现接口,Spring 会使用 CGLIB(Code Generation Library)来创建代理对象。CGLIB 是通过继续目标类来创建子类的方式进行代理的。
切面aspect(一个类,意思是要负责什么功能的,AOP要干嘛),切点pointcut,关照before after around;after还可以分为returning、throwing、finally
连接点,切点:
- package com.example.aspect;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.*;
- @Aspect
- @Component
- public class LoggingAspect {
- // 切点:匹配com.example.service包下的所有方法
- @Pointcut("execution(* com.example.service.UserService.*(..))")
- public void userServiceMethods() {}
- // 前置通知:在切点匹配的方法执行前记录日志
- @Before("userServiceMethods()")
- public void logBefore(JoinPoint joinPoint) {
- System.out.println("Before executing method: " + joinPoint.getSignature().getName());
- }
- // 后置通知:在切点匹配的方法执行后记录日志
- @After("userServiceMethods()")
- public void logAfter(JoinPoint joinPoint) {
- System.out.println("After executing method: " + joinPoint.getSignature().getName());
- }
- // 环绕通知:增强方法的执行,计算方法执行时间
- @Around("userServiceMethods()")
- public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
- long startTime = System.currentTimeMillis();
- Object result = joinPoint.proceed(); // 执行目标方法
- long endTime = System.currentTimeMillis();
- System.out.println("Execution time of " + joinPoint.getSignature().getName() + ": " + (endTime - startTime) + " ms");
- return result;
- }
- }
复制代码 userServiceMethods() 不是 JoinPoint,它是你自定义的 切点 方法,实际上它并不代表一个详细的方法,而是用来标识一组方法的集合,即你定义的切点(Pointcut)。
Deque双端队列
最后,如果“桶”里已经有其他键值对了(发生哈希冲突),HashMap 会用链表或红黑树来存储多个键值对。
HashMap 的目标是高效查找,而红黑树能很好地满意这一点,并且它与内存结构更契合。B+ 树则更得当大规模外存数据管理。
hashMap里面key hashcode value的关系
使用位运算的特性,判定元素是否必要迁移,如果 hash & oldCapacity == 0,元素保持在原位置;否则,迁移到原位置 + oldCapacity。这种方法制止了重新计算哈希值,提拔了扩容服从。
HashMap 存放 key-value 对的过程如下:
- 计算 key 的哈希值:通过 key.hashCode() 计算出哈希值。
- 计算桶的位置:将哈希值与数组大小进行取模,确定桶的位置。
- 处置惩罚冲突:如果该位置已有其他元素,使用链表或红黑树来存储新元素。
- 存放元素:如果该位置为空,则直接将元素放入该位置;如果有冲突,则放入冲突的链表或红黑树中。
hashCode() 的主要应用包罗:
- 在 哈希表 和 哈希集合 等数据结构中,决定对象的存储位置并进步查找服从。
- 与 equals() 方法一起用于 判定对象是否相称,尤其是在集合类中。
- 用于 缓存和散列存储,进步数据查找和存储服从。
- 在某些算法中用于 天生唯一标识符 或用于 分区 和 负载均衡。
- 作为 对象标识符,在某些场景下用于判定对象是否相同。
当 table.length 是 2 的幂时,位与运算可以更换取模操作,从而进步服从。详细地,h % table.length 和 h & (table.length - 1) 的结果是一样的,前提是 table.length 是 2 的幂。
头插法,循环链表
对象的引用范例数据
String基本数据范例?不是,引用范例;但是不用专程clone(),重新赋值就会改变对象的引用到新的string常量,也是深拷贝了。:(3)不可变对象:如果对象是不可变的(如 String),引用拷贝不会带来副作用,因为对象本身无法被修改。
- class Person implements Cloneable {
- String name;
- Address address;
- public Person(String name, Address address) {
- this.name = name;
- this.address = address;
- }
- @Override
- protected Object clone() throws CloneNotSupportedException {
- Person cloned = (Person) super.clone();
- cloned.address = (Address) this.address.clone(); // 手动深拷贝引用类型
- return cloned;
- }
- }
- class Address implements Cloneable {
- String city;
- public Address(String city) {
- this.city = city;
- }
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- }
- public class Main {
- public static void main(String[] args) throws CloneNotSupportedException {
- Address address = new Address("New York");
- Person person1 = new Person("Alice", address);
- Person person2 = (Person) person1.clone();
- System.out.println(person1.address.city); // 输出: New York
- person2.address.city = "Los Angeles";
- System.out.println(person1.address.city); // 输出: New York
- }
- }
复制代码 深拷贝:this.引用数据对象1.clone();引用数据对象1的引用数据对象同样clone……递归手动实现clone(),直到最后一层有引用范例的结束。

Integer 类在内部缓存了 -128 到 127 之间的值。这意味着在这个范围内的 Integer 对象会被复用,而不是每次都创建新的对象。
集合里面只能用包装类作为泛型对吗
编译器会自动调用包装类的 valueOf() 方法来创建对应的包装类对象
编译器会自动调用包装类的 xxxValue() 方法,好比 intValue()、doubleValue() 等,来获取基本数据范例的值
重载 和重写:多种调用和多态
发生在一个类;方法名一样,参数一定不同;返回范例可以不一样;非常不能比 父类的大;访问修饰符不能比父类的严酷;静态绑定
==和equals:
比力基本范例和引用对象;比力所在大概基本范例的值;没重写和==一样;String和包装类重写了,比力值;性能;==不能重写
- String s1 = new String("hello");
- String s2 = new String("hello");
- System.out.println(s1 == s2); // false,比较的是内存地址
- System.out.println(s1.equals(s2) ); // true
- String s3 = "hello";
- String s4 = "hello";
- System.out.println(s3 == s4); // true,比较的是内存地址
复制代码 hash码:快速定位在哈希表底层数组的存储位置;计算,桶的位置,有元素了,equals看有没有相同的,有忽略,没有插到链表大概红黑树;
泛型 给接口 类 方法 转达详细范例;编译时确定;复用、安全 不会castException 、约束、
class A<T>
<T> void
范例擦除:编译的时间检查泛型必须是定义的范例,都是在运行的时间全部换成Object,
反射 动态获取类的:结构 字段 方法 ,调用方法,修改字段值;框架、动态代理 AOP、注解开发、测试工具、插件化开发
可以访问私有成员;类的全限定名String:Class.forName()得到Class<?> clazz,clazz.
- getDeclaredConstructor.newInstance得到对象;clazz.getMethod得到方法;Field[] fields = clazz.getDeclaredFields();所有字段,.getDelaredField()特定字段
复制代码 method.invoke(对象,参数)
- 获取 Class 对象:Class.forName()、对象.getClass()、类名.class
- 获取构造函数:getDeclaredConstructor()、getConstructors()
- 获取字段:getDeclaredField()、getFields()
- 获取方法:getDeclaredMethod()、getMethods()
- 创建对象:constructor.newInstance()
- 调用方法:method.invoke()
- 访问和修改字段:field.get()、field.set()
- 获取注解:getAnnotation()、getDeclaredAnnotations()
stringbuffer:得当经常修改,多线程,有synchronized修饰线程安全;stringbuilder:经常修改,单线程;string不可变,每次修改,天生新对象
消息队列,有序性 BRPOP阻塞;判重全局ID;可靠,BRPOPLPUSH备份list
JUC
创建线程的方法(thread的run,其他三种 本质都是start()启动(new thread().start() );runnable,thread构造器,资源复用runnable实例;callable 返回值,futuretask 详细的是:Callable包装到futuretask,futuretask再包装到thread构造器;executor线程池,submit,shutdown)
创建(平凡java对象)、停当(调理器)、运行(run)、阻塞(等待锁)、等待(其他线程唤醒)、结束(run结束、非常)
创建 了 可以到 停当/运行;运行可以到等待——停当也可以;
等待阻塞(wait,notify / notifyall) 和 同步阻塞(获取synchronized代码块……)/sleep/join(子历程调用先运行完再继续父)/IO 区别
线程上下文切换:when:时间片用完;体系中断IO;被阻塞/等待 ;线程end了。
生存旧的;选下一个(调理算法);加载;实行
镌汰切换影响:好的调理算法、线程池别太大、CAS等(
- Java 中的 CAS 操作主要通过 java.util.concurrent.atomic 包中的原子类实现。
- 常用方法:
- incrementAndGet():原子增加。
- compareAndSet(oldValue, newValue):手动实现 CAS。
- CAS 是无锁并发编程的核心工具,适用于低竞争场景。
)
并发(executorService) | 并行(forkJoinPool,并行流,分布式): 时间分片照旧多核使用;单CPU;多I/O照旧多计算;进步使用率照旧吞吐量
同步(一个任务完成了才下一个,中心会阻塞,得当 简单、立刻就要结果的);异步(胜利不必等待,因为其他操作的到来,那接下来的什么时间继续——其他操作 回调函数Runnable对象.run()、变乱关照 swing监听等 、future对象.get, CompletableFuture对象.join,得当 网络请求、文件读写。好比netty,发送请求了去干别的事,响应过来了,用线程回调大概变乱关照)
线程池:核心线程;最大线程(什么时间才分配 非核心线程 );线程空闲时间;拒绝策略(非核心都满了 & 队列也满了;中止abort rejectedExecutionException; 调用者运行callerRuns 线程池里面的干不了,一样平常是主线程干;丢弃discard 不抛非常; 挤掉最旧discardOldest ) ;时间;任务队列(arrayblockingqueue、linkedblockingqueue前两个可以有界 synchronousQueue无存储 priorityBlockingQueue优先级调理 DelayQueue定时、缓存等 linkedTransferQueue数据、请求节点&CAS——高效);线程工厂
有不确定输出,反正如果有两个这里面的任务同时实行的话,肯定是同一时间一个在main一个在pool-1-thread-1
- public class CallerRunsPolicyExample {
- public static void main(String[] args) {
- ThreadPoolExecutor executor = new ThreadPoolExecutor(
- 1, 1, 60, TimeUnit.SECONDS,
- new ArrayBlockingQueue<>(1),
- new ThreadPoolExecutor.CallerRunsPolicy()
- );
- for (int i = 0; i < 5; i++) {
- final int taskId = i;
- executor.submit(() -> {
- System.out.println("任务 " + taskId + " 正在运行,线程:" + Thread.currentThread().getName());
- try {
- Thread.sleep(1000); // 模拟任务执行时间
- } catch (InterruptedException e) {}
- });
- }
- executor.shutdown();
- }
- }
复制代码 ThreadPoolExecutor
Executors无界队列newfixedThreadPool、newsingleThreadExecutor、newCachedThreadPool容易溢出,告急参数设置 不了,不容易排查Q
concurrentHashMap:JDK8之后的:CAS(并发冲突用 synchronized 锁住桶 办理,并发好)、红黑树(低落时间复杂度为O(logn));hashmap不是线程安全
- synchronized (锁对象) {
- // 同步代码
- }
复制代码 互斥(不用锁,CAS、concurrentHashMap) 占有且等待(一次性申请所有资源) 循环等待(资源标上全局序号,线程顺序申请) 不可剥夺(体系逼迫剥夺,java里面不常用)
B在A尝试获得resource 2 的时间肯定已经获得resource 2的锁了,所以死锁
- public class DeadlockExample {
- private static final Object resource1 = new Object();
- private static final Object resource2 = new Object();
- public static void main(String[] args) {
- Thread threadA = new Thread(() -> {
- synchronized (resource1) {
- System.out.println("Thread A: Holding resource 1...");
- try { Thread.sleep(100); } catch (InterruptedException e) {}
- System.out.println("Thread A: Waiting for resource 2...");
- synchronized (resource2) {
- System.out.println("Thread A: Holding resource 1 and 2.");
- }
- }
- });
- Thread threadB = new Thread(() -> {
- synchronized (resource2) {
- System.out.println("Thread B: Holding resource 2...");
- try { Thread.sleep(100); } catch (InterruptedException e) {}
- System.out.println("Thread B: Waiting for resource 1...");
- synchronized (resource1) {
- System.out.println("Thread B: Holding resource 1 and 2.");
- }
- }
- });
- threadA.start();
- threadB.start();
- }
- }
复制代码 怎么检测:jstack <pid>、threadmxbean编程
synchronized 底层:代码块/方法;JVM的monitor锁;一个线程调用,monitorenter,看变量 monitor进入数 是0,变1 ;不是0 阻塞;退出同步代码块,monitorexit 进入数变1,
和reentrantLock:隐式,监视器锁,周期内部实行 | lock & unlock;等待锁可以响应中断,线程可以尝试获取锁;高竞争——reentrantLock公平锁/可中断锁,syn有优化——偏向锁/轻量级锁;ree释放不好会死锁,finnally确保释放锁
非公平锁吞吐量高;公平锁 先到先得 性能较低
- 顾客 B(使用 ReentrantLock):如果顾客 B 在等待锁时听到了紧急关照(中断信号),他可以立刻放下餐具(制止等待锁)并脱离餐桌(处置惩罚中断)。
- 顾客 A(使用 synchronized):如果顾客 A 在等待锁时听到了紧急关照,他无法立刻响应,必须等到获取到锁后才能处置惩罚中断。
无锁 偏向锁(只有一个thread来获取) 轻量级(第二个来,CAS获取锁:锁对象的mark word换成指向线程的栈帧的锁纪录的指针 ,获取失败自旋等待(不停CAS重试),没有mark word指向锁纪录) 重量级(>=1个自旋等待 超过最大次数 大概其他自定义的——挂起阻塞,os调理,上下文切换开销大)
乐观锁 CAS(比力并交换:硬件CPU直接控制,比力 :内存位置、预期值、新值——内存位置的值==预期值 才更新)大概版本号
没有锁,冲突的话必要显式处置惩罚——重试、回滚
得当:高并发、读多写少、分布式;核心思想是假设冲突很少发生
冲突许多,重试会许多,照旧悲观锁吧
CAS:ABA题目(加版本号);多个对象不可以(集合到一个对象然后atomicreferrence;事务性内存 STM 鉴戒数据库的事务 原子性 回滚重试 乐观锁;synchronize大概reentrantlock);自旋性能不好(超过次数就阻塞,Semaphore悲观锁 允许证 会阻塞);
JVM
对象:类载入(载入一次:加载(全限定名get二进制字节流——加载.class文件/网络下载/动态代理,转换 成类的元信息,天生java.lang.Class对象)、验证(4个 格式元码符)、预备(只是零值)、剖析(符号引用到直接)、初始化(静态代码块&静态变量赋值))
=》 分配内存(指针碰撞or空闲列表;多线程时:CAS更新分配指针 or 线程预先分配专属区域)
=》 零值初始化
=》 对象头(mark word 对象哈希值&GC分代年龄&锁状态标志……;klass poniter 类.class的所在;数组长度)
=》 实行构造方法(继续链的初始化)
new、反射Class(只能无参构造) Constructor、clone(如果要深拷贝——递归下去的所有引用数据范例都实现clone())、反序列化(serializable接口)
- Java原生序列化:必须实现Serializable接口:
- Person person1 = new Person("fsx", 18);
- byte[] bytes = SerializationUtils.serialize(person1);
- Person person2 = (Person) SerializationUtils.deserialize(bytes);
复制代码
- JSON、XML、Hessian等序列化方法:不必要实现Serializable接口,它们通常基于反射或其他机制来序列化和反序列化对象。
类加载器 4层(启动类、扩展、应用步伐、自定义类)当前类加载器 检查缓存 =》 没有则委派给父加载器 =》 递归向上
双亲委派——唯一加载、安全 防止伪冒核心类库、模块化
JDK1.8:
共享:堆(实例和数组和字符串常量池——GC管理)
私有:假造机栈(每个线程一个,每个方法一个帧栈) & 当地方法栈 & pc步伐计数器(每个线程一个)
当地内存:元空间 和 直接内存
运行时常量池(类的符号引用、字符串字面量等) 和 字符串常量池:运行时常量池 时大时小;之前永久代的大小固定 不好,放元空间,os调解采取,制止内存不敷;字符串常量池放堆,堆内存管理更灵活且更得当字符串这种频仍访问和采取的场景
- public class InternExample {
- public static void main(String[] args) {
- String str1 = new String("hello"); // 在堆中创建一个新的字符串对象
- String str2 = "hello"; // 直接使用字符串常量池中的字符串
- String str3 = str1.intern(); // 将str1的内容添加到字符串常量池(如果池中不存在)
- System.out.println(str1 == str2); // false,str1是堆中的对象,str2是常量池中的引用
- System.out.println(str2 == str3); // true,str3是常量池中的引用,与str2相同
- }
- }
复制代码 分代网络——新生代:复制(from和to两个区,只有一个区域放东西);老年代:标记扫除(根对象开始,遍历两次:标记和扫除,时间长&有碎片)和标记整理(也是两次,整理是移到内存一端):两个都是两次遍历,得当对象存活率较高的老年代
判定垃圾否?引用计数 但是对象只剩下循环引用办理不了;可达性分析(GC roots)
都是三标记1采取。
CMS:一个GC、GC线程+用户、多个GC、多个用户线程和一个GC 并发扫除。标记-扫除——内存碎片
G1:一个GC、用户线程和一个GC、多个GC、多个GC 线程进行筛选采取(垃圾最多的region),基于堆分区,将采取工作会合于垃圾最多的区域,制止全堆扫描。硬件要求高。标记-整理/复制
redis
购物车 说出 可以用到redis的;
string:string+json得当不咋变动;分布式锁;共享session;计数
list:消息队列;
set:点赞、共同关注,抽奖
zset:排行榜;电话姓名排序(score都设置一样,ZRANGEBYLEX)
hash:购物车;得当变得多的;缓存对象
bitmap 个人签到、统计签到、登录和登出
hyberloglog统计网页uv
geo附近所在、打车
stream发布/订阅 只能实时(发布,订阅,订阅列表,发布的东西立刻推送 循环遍历发送,不生存消息);消息队列组(组内 负载均衡;组间可以重复用; XACK XPENDING瓦解用)
Redis当做消息队列和专用的消息队列:RabbitMQ这种,区别在于:专用的可以存到磁盘,Redis AOF恒久化可以但是发布 订阅不可以;redis会丢数据(异步主从复制大概AOF异步刷盘),专业的摆设了一个集群;stream消息积压,直接把旧消息删除。是不是这些区别?
整个redis服务器存的数据,整体结构是什么样的
键值对怎么存的
SDS怎么办理了C char*数组的三个题目?
rehash,之前说过吗?集合hash的扩容,double,长度2的幂次方,是不是全部都移动?怎么移动
redis哈希表不是,哈希表2,渐进rehash,增、删查怎么弄的?什么时间要rehash
zskipListLevel;zskipListNode跳表,zset里面,怎么找到score=;值= 的元素?层的数量怎么定?比均衡树幸亏哪?跟集合哈希不一样的rehash?
listpack和quicklist和压缩列表:谁改进了,谁包含谁?
AOF日志,是读内存数据库的数据,写的是下令不是数据;
3种参数用fsync,决定什么时间从缓存到文件写到磁盘;
AOF满了,重写AOF,只有最后一次的下令;先复制页(阻塞父历程),页的物理所在都一样,指向的数据都一样,如果主历程改了,子历程写时复制(写时复制,子历程有一个数据副本,就是为了不用加锁进步性能;阻塞父历程);复制之后呢,再改了?AOF重写缓存;redis单线程实行的,所以AOF会阻塞写下令,always的写+AOF追加是主历程,AOF重写是子历程bgrewriteaof;每个历程创建自己的假造所在空间;
大key,阻塞:网络,客户端;写入AOF,always的时间肯定阻塞,其他策略不是同步;重写AOF大概RDB快照,页表复制、写时复制 会阻塞;删除del不行unlink可以异步;
RDB 是拍照,AOF 是录像,混淆是先拍照再录像。
LRU最近最少使用,时间,一样平常链表,不行耗空间,照旧纪录上一次使用 的时间;LFU最近最不常使用,4.0以后用这个,频次,redisObject里面一个24bit的变量,前16是时间戳,后8频次,频次有讲究:先衰退再增加,衰退有一个参数决定,在config,增加也是,和前次访问的时隔断得越久,衰退越多;Logc越大,约不容易增的多
连接(从 发起,主告诉 从两个量)-》第一次同步,三个时期主还会实行写下令(主历程),下令写在replication buffer,从自己加载RDB好了,关照主,主把缓冲区的再送过来,同步了,全量复制-》下令传播(写下令在replication buffer)。主 fork和网络带宽题目,多层(分担写同步);增量复制,写下令也会写在repl_backlog_buffer,从在网络好起来了,发偏移量给主,主把 这个 到 自己写到的 地方的数据,发去,这个repl_backlog_buffer多大符合?一次宕机,网络恢复的时间,写下令能来多少 ,最少就设置这么多,怕被覆盖,覆盖了就得全量复制了。
增量复制,在网络故障,之后从服务器 确定 网络状态 精良的时间会发送复制偏移量,平常也会1s一次发给主服务器;两个buffer 哪个从节点一人一个,哪个从节点共享;哪个两个阶段都存在,哪个只在增量复制存在; 直接覆盖 or 连接断开重新全量复制
主从节点不一致,可能A有一个写下令,主节点写入了,下令传播还没到从节点,从没写入,B读从的时间其实是旧数据。办理办法是外部步伐监控主从复制差异,超过阈值断开连接。脑裂:主节点只是和从都断开了,但是自己还能工作,断开的时间还实行写下令,哨兵推出新的主,原主变从,旧数据清空全量复制,断开时间的数据丢失-》断开的时间禁止写,超过多少从 节点才能写,网络延迟超过 多少 不能写
哨兵集群:主观下线(单个判定,网络状态);客观(集群投票,超过quorum赞同);下线的 主从切换:哨兵选Leader,> 1/2大概>= quorum,哨兵宕机后留下的不能少于quorum大概少于即是1/2,不能判定也不能切换。直接 把 quorum 设置1/2 +1,选leader判定>=quorum。为什么哨兵节点应该是奇数?
哨兵:监控,主观,客观下线,主从切换之后监控旧主,变成从;选主,选出leader,leader选出主(怎么选,选了怎么故障转移的,1过滤3考察,);关照,slaveof no one关照新主,slaveof 新主ip_port关照,关照客户端(发布者订阅者,新主从切换+swtich-master,频道许多,其他切换进度的频道)。 主 发布频道_sentinel_:hello,哨兵订阅,哨兵发自己的信息上去,构成哨兵集群,再INFO主,得到所有从的信息
节点正常工作吗——ping、info(role、connected_clients)、telnet/netcat、监控(Prometheus、Grafana 等监控工具)、cluster info(cluster_state)
雪崩 大量请求的大量数据过期(过期时间设置的平均一点;来互斥锁,一个资源一个请求去Mysql去查就好了,超时时间;直接返回空/默认;设置永久,实际 后台异步更新到缓存,比起后台自己检查然后更新,照旧业务线程发现过期 消息队列 关照 更新好,后台还会一开始缓存预热(redis缓存预热:业务刚上线的时间和radis宕机之后),进步性能) & Redis宕机(限流比熔断,还能允许一些请求通过),要去mysql;
击穿 热点数据不在缓存(雪崩的特殊环境);
穿透(不在缓存也不在Mysql,有可能黑客故意(提前检查);布隆过滤器(哈希会冲突,一个数据N个哈希函数N个桶是为什么,一个对应的所有桶都1 -》↓误判率,说不在的一定不在,说在的不一定在);直接缓存的 空/默认)。
布隆过滤器的应用场景
- 缓存穿透防护:在 Redis 中,将查询过的不存在的 key 存入布隆过滤器。当请求到来时,先通过布隆过滤器判定是否存在,制止查询数据库。
- 大数据去重:比方统计独立用户 ID、网页爬虫 URL 去重。
- 垃圾邮件过滤:快速判定邮件所在是否在黑名单中。
- 分布式体系协调:检查某个资源是否已被其他节点处置惩罚。
并发题目 :懒加载、cache aside数据旁路; 先删缓存(延迟双删)、先改数据库(锁;加个过期时间;缓存删除失败怎么办:消息队列重试;Binlog+消息队列+重试缓存)?
分布式锁:String——二进制安全(不会因为数据中包含特殊字符(如空字符 \0、换行符 \n 等)而导致数据被截断或误解。)、快速简单,原子性——NX只有锁不存在才创建,PX制止死锁,my_random_value随机且唯一,只有一个用户获得锁——如果锁已经存在,其他客户端会获取失败(nil)
解锁——lua脚本原子性地检查my_random_value和客户端能不能对上,再删,不是直接DEL,竞争条件(,锁的键是复用的,如果不使用 Lua 脚本进行检查,直接删除锁键可能会误删其他客户端持有的锁。)
1、直接——写得少、读得多
写redis,写mysql——数据丢失
写mysql,写redis redis更新失败
写mysql,删除redis 速度慢
删除redis,写mysql 数据不一致
删除redis,写mysql ,删除redis 性能不好
binlog异步更新(监听Binlog,剖析) 好,一致性要求高的地方
2 消息队列——高并发、高可靠性 要能恒久化、能去重的消息队列——kafka 监听binlog 发布,消费者同步,重试
3 TCC——对一致性要求极高 try confirm cancel ——try删除redis,confirm数据库的更新,失败cancel redis的try
4 分布式数据库中心件——实时同步数据库变更到 Redis 的场景(如实时数据大屏) ,canal,setinel,Seata
- Redis 是内存数据库,写入操作依赖于网络和内存状态,容易受到网络抖动、Redis 宕机等题目标影响。 速度快但是不可靠,不能回滚
- MySQL 是恒久化数据库,写入操作会落盘,可靠性更高。
rabbitMQ
优点:解耦、异步、削峰
要办理:可用性、重复消费、消息丢失——发送去RabbitMQ :事务大概confirm机制(高吞吐量);在RabbitMQ 中:恒久化(队列+消息),集群摆设&镜像;发送去消费者:basicAck和死信队列和消息补偿(消费者自己检查)、顺序性、一致性
性能:kafka 大数据》rabbitMQ》avtiveMQ
可靠(消息恒久化、消息确认机制和事务机制)路由(交换机和绑定规则)高可用(集群摆设和镜像队列),扩展(高并发,插件扩展)协议(兼容AMQP、STOMP、MQTT等多种消息协议)多语言。
Web管理插件(扩展功能如消息追踪、性能监控等)强,功能全面不迷茫。
直接绑定:Direct Exchange 会通过路由键直接将消息发送到与该路由键匹配的队列。
模式匹配绑定:Topic Exchange 允许使用通配符进行模式匹配绑定,基于消息的路由键来决定队列是否接收消息。
交换机:直接 完全匹配、扇形 广播、主题 通配符匹配 、头 路由键+头
Broker 的责任还包罗管理交换器、队列的设置和消息的恒久化(如果启用了恒久化)。
消息有序:一个队列一个消费者,需多个队列;就一个队列一个消费者,消费者内部队列列队,多个线程并发处置惩罚
os的消息队列:内核实现,单机历程通讯;FIFO;单一生产者 消费者(消费者多 ——互斥锁/信号量);优先级
OS
线程/历程:资源分配/任务调理;创建和切换 开销;之间的通讯;并发(一个历程不影响其他,一个线程会让一个历程end)
历程调理:FCFS(IO 麋集不得当)、优先级(怎么制止饥饿的?)、多级反馈队列MLFQ(多个不同优先级的队列,刚开始进入都是最高优先级队列,时间片用完还没好,下降到低优先级的)、时间片RR(太短切换多影响性能,太长退化成FCFS)、短作业优先SJF(长作业的等太久)
线程:时间切片、抢占式(综合考虑:等待时间、优先级等)
历程通讯:信号(异步关照,历程控制)、信号量(计数器,访问同一资源)、无名管道(子历程、兄弟历程;在内核中开发一块存储空间——环形队列,Linux 默认64KB)、著名管道(没关系的历程,磁盘文件)、套接字(网络通讯)、共享内存(最快,信号量/互斥锁 办理冲突)、消息队列(比管道灵活,可以随机查询 恒久化)
- 数据共享:共享内存、内存映射文件。
- 内核管理的缓冲区:管道、消息队列。
- 同步机制:信号量、互斥锁、条件变量(等待,条件同步)。
- 关照机制:信号。
- 数据传输:套接字、文件。
线程更高效:共享资源 没有额外IPC机制、切换开销小 栈指针和寄存器的切换、创建开销也小 只用开发一个栈空间(假造机栈和当地方法栈和pc,放线程的局部变量、函数调用信息(如返回所在、参数)等)、并发服从高 共享历程资源
变乱循环机制:
- 实行所有同步任务。
- 检查微任务队列,实行所有微任务。
- 检查任务队列(回调函数),实行一个任务。
- 重复上述步调。
所以一个线程处置惩罚多个任务
用户态 、内核态(特权模式 直接操作硬件):体系调用(syscall 文件操作 网络访问 历程管理)、非常处置惩罚(除零错误 非法内存)、硬件中断(磁盘 网络数据接收)
防直接操作硬件、用户步伐瓦解了不影响整个体系、恶意用户恶意篡改磁盘、历程调理和内存管理必须在内核态
在用户态,体系调用/内核api调用 换到内核态 直接访问会触发段错误(Segmentation Fault)。
用户态空间一样平常32位的是4GB,内核可以访问整个物理内存
计网
输入URL到……
欣赏器剖析:协议 域名 路径(从 / 开始) 端口号 锚点 参数(?key=value) =》 DNS剖析:域名-Ip 在当地有缓存吗——没有 域名体系 DNS服务器 =》 跟ip的服务器TCP连接 =》 HTTP请求,请求行有GET/POST、URL路径、HTTP版本,请求头有欣赏器信息、缓存控制,请求体发送数据 =》 服务器根据路径找静态资源,动态交给spring大概 =》 发响应,行(200 OK)头(缓存控制 内容范例 日期)体(HTML CSS等) =》 欣赏器渲染 =》 关连接
DNS剖析:欣赏器缓存=》操作体系缓存=》当地DNS缓存 =》当地DNS 递归(当地挨个查 根、顶级.com、权势巨子.baidu.com,。直到最终找到域名的 IP 所在)大概DNS客户端迭代(依次查 当地DNS、根、顶级、权势巨子)。如今用的多的是递归,减轻客户端压力,客户端一次请求就不用管了,让当地服务器去找,缓存也更方便=》当地、操作体系把ip存起来
DNS sever拒绝服务;插入假DNS中毒;中心人 DNS欺骗
HTTP明文,HTTPS要摆设SSL/TLS证书,搜刮引擎偏好HTTPS,端口号和前缀不一样
GET和POST:获取数据照旧提交数据;GET数据在url上不安全,POST的在请求体里面如果HTTP不加密也是不太安全;能携带的数据大小 GET是2到8KB POST没有限定;传输方式 POST在请求体;幂等性导致适不得当缓存和加到书签
幂等性强调的是多次请求不会导致服务器状态的额外变化,而不是响应内容不变
PUT和DELETE都是幂等的,多次和一次的变化一样
HEAD OPTIONS PATCH
TCP和UDP:连接;可靠(序列号 确认 重传);流量控制 拥塞控制;保证顺序吗;传输服从;使用场景 (实时性高;网页欣赏;文件传输);支持 单播 or广播 多播 组播;分片,MSS是谁的;TCP是基于字节流的,而UDP是基于报文的
UDP可靠性:加ACK;加重传,限定最大次数;数据包编号;FEC前向纠错,部门数据丢失也能恢复;RUDP、QUIC(HTTP3)等可靠UDP协议
TCP可靠的原理:连接受理 防止数据残留/资源浪费;数据分快和序号 报文段 标上起始字节序号,和长度字段一起在接收端重组;流量控制 接收端设置一个流量控制滑动窗口,防止接收端缓冲区别溢出;拥塞控制 慢启动 拥塞制止 快速重传 快速恢复 ,发送端实现拥塞窗口,防网络瘫痪;确认应答和超时重传 不丢失 ;错误检测和去重 校验和
你(客户端 SYN)来我应(SYN-ACK),你再定(ACK)
我(客户端 FIN)说完,你(ACK)听完,你(FIN)再说,我(ACK)再见
why not两次握手:过期的syn来到服务器,误以为要连接,开启连接状态,但是客户端可能关了,收不到ACK,资源浪费
why not 三次挥手:还有数据没发到客户端,可能数据丢失。server发送ACK了,还有数据没发完的赶紧发已往,再关照客户端可以关了FIN,客户端再ACK
第四次挥手 等2MSL(最大报文生存时间,超过就会被丢弃):留点时间怕ACK丢失客户端早早关了收不到server重发的FIN,就重发不了ACK;残留的TCP报文全部消失,不影响下个连接的数据
设计模式
单例:构造方法私有,一个私有静态实例变量;一个public静态方法获取这个实例:如果是Null,进同步代码块重复检查——多线程安全,然后new;否则直接返回这个变量
实现:都是线程安全
饿汉式 private static final Singleton instance = new Singleton ()类加载的时间就初始化了,如果背面没用到浪费
懒汉式 private static Singleton instance ; private static synchronized get(){ if(instance==null) instance = new;return instance }每次get都同步,性能低
双重检查private static volatile Singleton instance ; private static get(){ if(instance==null) {synchronized (Singleton.class ){if(instance==null) { instance = new;}}return instance只有不存在才同步,volatile干嘛的——线程改的东西立马更新到主内存从而其他线程知道工作内存的内容失效(缓存一致性
volatile 的实现依赖于这些底层机制:
- 当一个线程修改了 volatile 变量时,JVM 会向 CPU 发送一个信号,逼迫将修改后的值写回主内存。
- 同时,JVM 会通过缓存一致性协议,关照其他 CPU 核心该变量的缓存已失效。
- 其他线程在读取 volatile 变量时,会发现自己的缓存已失效,于是会从主内存中重新加载最新的值。
);一条下令的多条指令,不重排(
- instance = new Singleton(); 这行代码实际上分为 3 步:
- 分配内存空间。
- 初始化对象。
- 将 instance 指向分配的内存所在。
- 如果发生指令重排序,可能会导致步调 3 在步调 2 之前实行。此时,其他线程可能会看到一个未完全初始化的对象。
)
静态内部类 private static class Holder{static final Singleton instance = new Singleton ()} return Holder.instance 性能和安全性都较好
罗列单例 public enum Singleton {INSTANCE;} 调用:Singleton singleton = Singleton. INSTANCE获取实例——Effective Java保举的实现方式。可以序列化、防止反射
Spring 默认的 Bean 作用域就是单例(Singleton),有没有线程安全题目?有可变的成员变量就会有,要么只使用不可变成员变量,要么用threadlocal(线程隔离:
- 数据库连接受理(每个线程使用独立的连接)。
- 用户会话信息存储(每个线程存储当前用户的信息)。
),大概锁
工厂设计:产物接口/抽象类=》产物类=》工厂接口/抽象工厂类=》工厂类 实现哪种产物对象返回
- 简单工厂模式:直接一个工厂类,根据传来的范例 参数return对象,违反开闭原则(只扩展添加不修改)
- 工厂方法模式:工厂抽象类加上,一个产物类对应一个工厂类。加一个产物类就得加一个工厂子类
- 抽象工厂模式:一个工厂类里面有一组产物类,变复杂了
生产者消费者:
workerThread 在 Thread.sleep(1000) 处被中断,抛出 InterruptedException。 捕获非常后,其实仍然不满意Thread.currentThread().isInterrupted()条件,所以调用 Thread.currentThread().interrupt() 重新设置中断状态。 上层的 while (!Thread.currentThread().isInterrupted()) 检查到中断状态为 true,退出循环并结束任务。
- Thread workerThread = new Thread(() -> {
- try {
- while (!Thread.currentThread().isInterrupted()) {
- System.out.println("Working...");
- Thread.sleep(1000); // 可能抛出 InterruptedException
- }
- } catch (InterruptedException e) {
- // 捕获 InterruptedException 但没有重新设置中断状态
- System.out.println("线程被中断,但中断状态丢失");
- }
- });
- workerThread.start();
- // 主线程休眠一段时间后中断 workerThread
- Thread.sleep(3000);
- workerThread.interrupt();
复制代码 如果代码中调用了可能抛出 InterruptedException 的阻塞方法(如 wait()、sleep()、join() 等),最幸亏捕获 InterruptedException 后重新设置中断状态(即调用 Thread.currentThread().interrupt())。
1、Queue+锁和wait/nofity:粒度太粗性能低——锁住,如果满了,wait,否则生产 放入,唤醒消费者notify;锁住,如果空的,等待,否则消费 取出,唤醒生产者
2、BlockingQueue:直接put/take同步实现了同步,性能好又简便
- public void put(E e) throws InterruptedException {
- if (e == null) throw new NullPointerException();
- final int c;
- final Node<E> node = new Node<E>(e);
- final ReentrantLock putLock = this.putLock;
- final AtomicInteger count = this.count;
- putLock.lockInterruptibly();
- try {
- /*
- * Note that count is used in wait guard even though it is
- * not protected by lock. This works because count can
- * only decrease at this point (all other puts are shut
- * out by lock), and we (or some other waiting put) are
- * signalled if it ever changes from capacity. Similarly
- * for all other uses of count in other wait guards.
- */
- while (count.get() == capacity) {
- notFull.await();
- }
- enqueue(node);
- c = count.getAndIncrement();//实际是get then increment
- if (c + 1 < capacity) //刚刚生产了,唤醒一个正在等待 notFull 的生产者线程,使其可以继续尝试入队。
- notFull.signal();
- } finally {
- putLock.unlock();
- }
- if (c == 0) //现在有一个元素了,唤醒阻塞的消费者,快来
- signalNotEmpty();
- }
复制代码 策略模式:抽象策略 (office ),详细策略(docx、xlsx),环境(根据传来的type返回详细策略对象,对象放在map——是static final,在static代码块初始化new对象确定所有详细策略类 持有策略对象的引用,并根据必要调用策略。)
- public interface OfficeHandlerStrategy {
- void handlerOffice(String filePath);
- }
- public class OfficeHandlerDocxStrategy implements OfficeHandlerStrategy {
- @Override
- public void handlerOffice(String filePath) {
- System.out.println("处理docx");
- }
- }
- // 省略 OfficeHandlerXlsxStrategy/OfficeHandlerPptxStrategy 类
- public class OfficeHandlerStrategyFactory {
- private static final Map<String,OfficeHandlerStrategy> map = new HashMap<>();
- static {
- map.put("docx",new OfficeHandlerDocxStrategy());
- map.put("xlsx",new OfficeHandlerXlsxStrategy());
- map.put("pptx",new OfficeHandlerPptxStrategy());
- }
- public static OfficeHandlerStrategy getStrategy(String type){
- return map.get(type);
- }
- }
- //测试
- public class OfficeHandlerStrategyClient {
- public static void main(String[] args) {
- String filePath = "C://file/123.xlsx";
- String type = getFileExtension(filePath);
- OfficeHandlerStrategy strategy = OfficeHandlerStrategyFactory.getStrategy(type);
- strategy.handlerOffice(filePath);
- }
-
- private static String getFileExtension(String filePath){
- // 解析文件名获取文件扩展名,比如 文档.docx,返回 docx
- String fileExtension = filePath.substring(filePath.lastIndexOf(".")+1);
- return fileExtension;
- }
- }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |