- 博客(41)
- 收藏
- 关注
原创 PageHelper实现分页原理
整个流程如下:1、当调用startPage进行分页时,会创建一个Page对象,填充分页属性,并保存到当前线程ThreadLocal,
2025-04-02 18:20:39
1024
原创 模板方法模式
模板方法模式,就如同其名字一样,定义了方法的基本骨架。其定义了算法/方法的骨架,并且向外部开放了一些需要由子类来负责具体实现的算法/方法。其主要包含了模板方法、基本方法和抽象方法,模板方法就是一个骨架,可能由多个基本方法和抽象方法组成,而基本方法则是类中自己实现的方法,抽象方法就是需要被多个类具体实现的方法。我们需要使用抽象模板类,只需要定义具体类继承抽象类,并且重写需要具体实现的方法即可,可以提高代码的复用性。
2025-03-07 16:14:51
347
原创 装饰者模式
装饰者模式是结构型设计模式的一种,装饰者模式可以在不修改原代码的基础上,动态的添加一些其他的功能,并且通过不同的装饰者实现功能的组合。
2025-03-07 15:12:45
296
原创 ReentrantLock源码解析
如果方法1返回true,则表明线程成功获取到了锁,如果返回false,则表示竞争锁失败,就会执行方法2,方法2中的参数调用了一个方法该方法会将当前线程包装成一个结点放到等待队列的尾部。当线程获取不到锁时,会与线程竞争公平锁的逻辑相同,会包装为结点放入等待队列,锁定前驱结点,当前驱结点获取锁时才会尝试再次获取锁,而不是一直在等待队列中尝试获取锁,这样太浪费性能了。当调用lock方法时,会调用sync变量的lock方法,而Lock只是一个抽象类,调用时会根据不同的子类执行不同的方法。
2025-03-06 21:53:50
624
原创 工厂模式:简单工厂、工厂方法以及抽象工厂
工厂模式是一种创建型设计模式,其作用是将对象的创建和使用进行解耦,用于提高代码的可维护性和可扩展性。通过提供给外部负责创建对象的工厂类,外部类通过工厂来创建对象。工厂模式包括简单工厂、工厂方法以及抽象工厂三种模式。
2025-03-05 21:35:15
621
原创 对象初始化时成员的执行顺序以及原理
当调用一个类的初始化时,首先判断该类有没有进行类加载阶段,该类继承了父类,父类也没有进行类加载阶段,因此父类先进行类加载阶段,在类加载阶段的初始化阶段执行了静态代码块的逻辑以及静态变量的显示赋值,在父类和子类都进行类加载阶段后,执行子类的初始化方法,而在其构造方法中,会被中转到父类的构造方法,因此先执行父类的实例代码块和实例成员的显式赋值,然后在执行父类的构造方法,最后调用子类的实例代码块、显式赋值以及子类的构造方法。:判断该类是否进行了类加载,如果没有执行类的类加载。:为对象分配内存空间。
2025-02-10 14:18:52
431
原创 SpringBoot启动流程(二)
填充容器前的准备:获取之前准备的环境,并设置到容器中,对容器的其他属性进行设置,并且会将主类注册到容器中填充容器:对容器的一些属性进行填充设置,并且会解析ComponentScan注解,对其属性进行设置,扫描指定的包,将配置类注册到Bean容器,在该阶段还会进行自动装配,获取Import注解,调用该类的方法,会加载META-INF/spring.factories中的自动配置类,通过@Condition等注解对配置类进行过滤,最后注册到容器中。
2025-01-23 18:01:27
989
原创 SpringBoot启动流程(一)
我们今天来通过源码分析SpringBoot的启动流程,本文中所使用的SpringBoot的版本为2.7.12。环境准备阶段首先会根据web服务类型创建相应的环境,并且会填充相应的系统环境和JDK环境信息,以及填充主方法传入的参数,读取配置文件例如application.yaml等文件的属性进行填充。
2025-01-22 17:33:27
663
原创 MyBatis执行一条sql语句的流程(源码解析)
加载配置文件:通过类加载器ClassLoaderWrapper以及一些其他的类加载器加载mybatis配置文件获得输入流。SqlsessionFactory的创建。
2025-01-17 20:47:07
1764
原创 JDK动态代理源码解析
自定义一个类实现InvocationHandler接口,调用Proxy的newProxyInstance在运行时会创建一个代理类,这个代理类继承了Proxy并且实现了被代理类接口,创建代理对象时传递了传入的InvocationHandler实现类,通过父类完成初始化和InvocationHandler的赋值,当调用代理对象的方法时都会中转到InvocationHandler实现类的invoke方法中,通过增强逻辑后,通过反射调用原方法。步骤1:根据接口和类加载器通过反射构造代理类。
2025-01-05 20:12:20
372
原创 CGLIB动态代理源码解析
通过ASM字节码库生成被代理类的子类即代理类,需要自定义MethodInterceptor重写intercept方法,当生成代理对象,通过代理对象调用方法时,会中转到自定义的方法拦截器的intercept方法中,而方法拦截器的设置是在生成代理类后、通过反射创建代理对象前,将方法拦截器写入常量ThreadLocal中,代理对象获取方法拦截器时从ThreadLocal中获取。
2025-01-05 18:44:22
907
原创 HashMap源码解析
HashMap相信大家在日常业务中也经常使用,其使用哈希表+链表的数据结构,使得我们可以利用哈希表的特性快速取出数据,又通过链表的形式来避免由于哈希冲突导致数据覆盖问题,我们今天通过JDK18的源码来分析HashMap的各种方法执行原理。我们可以发现这三个构造函数内都没有进行哈希表的初始化,只设置了负载因子和表的阈值,负载因子的默认值位0.75或者传入的参数,而阈值为大于传入哈希表容量的最小2的幂次方。
2025-01-01 20:32:13
930
原创 MVCC实现原理以及解决脏读、不可重复读、幻读问题
如上图,事务102插入了id=10的数据,由于Read View事务101本来是对事务102插入的数据是不可见的,但是由于事务101对插入的数据进行了修改,而当前修改的数据对当前事务是可见的,就导致了事务101读取了新插入的数据,造成了脏读的问题,但是在业务中发生这种情况的概率相当低。@2:如果m_ids中包含trx_id,并且trx_id ==creator_trx_id,说明该事务是当前事务生成Read View视图时未提交的事务,但是该事务是当前事务,因此是可见的。
2024-12-29 15:55:49
1229
原创 Sentinel底层原理以及Redis如何实现限流原理
关于微服务项目中的服务保护,我们会使用第三方的组件,例如Sentinel或者Hystrix,今天我来总结一下我对于Sentinel的学习。
2024-12-23 18:16:39
1289
原创 Redis分片集群学习总结
但是也存在一些缺点,由于主从集群是单个主节点,因此其大量写性能较差,我们今天来学习分片集群,采用多主多从的架构,多个主节点存储Redis不同部分的数据,并且负责写入数据,其从节点负责读取数据,大大提高了并发写操作的性能。通过散列插槽即slot的方式,redis集群一共有16386的插槽,在创建集群时会将这些插槽分配给每个主节点,当进行数据的写入和读取时,会基于CRC16 算法对key做hash运算,然后与16384求余,得到数据写入或读取的slot,然后到相应的主节点中进行写入或者读取。
2024-12-22 15:29:45
959
原创 Redis的主从集群以及哨兵机制学习总结
如果主节点被认为是客观下线,那么最先发现主节点宕机的哨兵就会进行故障转移,从从节点中推选出一位主节点,一般是根据这些从节点与主节点的最晚断开连接时间、在缓冲环中的最大offset或者最多运行时间(同步了主节点最多的数据),然后将推选出的主节点设置为其他的从节点的主人,将故障的主节点设置为故障状态,等待其重启后认主。当从节点向主节点发送数据同步的原理时,主节点会判断从节点replid是否和自己相同,如果不相同,说明没有进行数据同步,就会向从节点进行全量同步,如果相同,就会进行增量同步。
2024-12-20 20:26:39
1012
原创 微服务项目所用技术栈(Nacos+OpenFeign+Sentinel+Seata+ElasticSearch)
对于一个微服务项目,我们需要学会哪些技术栈,以下是我学习微服务完成一个项目开发后的总结。
2024-12-18 20:57:26
1029
原创 RabbitMQ延迟消息的实现
在一些业务中,可以用到延迟消息,比如我们在成功下单一个商品后,需要立即付款,为了避免商品库存一直被占有,我们会给商品设置一个支付时间,如果在这段时间没有支付成功,就会恢复库存,删除订单,对于订单支付的超时删除我们是通过延迟消息来实现的,让消费者在支付超时之后查询用户是否支付,如果支付成功直接返回,如果支付失败就恢复库存删除订单。:给消息设置延迟时间,当将消息放入MQ时,MQ的交换机不会立即将消息放入队列,而是会在交换机中暂存延迟时间过后将消息路由到队列中,可以让队列处理延迟消息。
2024-12-06 17:24:31
1162
原创 RabbitMQ保证消息的可靠性和幂等性
我们在上面保证了消息的可靠性,也即消息至少被消费一次,在某些情况下消息存在重复消费,例如,在我们开启消费者确认机制后,当消息被消费者处理后会返回ack,队列收到后会将队列中对应的消息删除,如果此时网络产生波动,导致队列没有收到ack,队列会以为没有消费过该消息,再次将消息分发给其他消费者,造成了消息的重复消费。Auto:自动处理。默认情况下,MQ会将收到的消息放入内存中,来减少消息收发带来的延迟,如果MQ突然宕机,那么内存中的消息就会丢失,因此我们要让交换机、队列和消息持久化,默认情况下它们都是持久化的。
2024-12-05 20:43:16
1350
原创 RabbitMQ的简单使用
在很多业务中都可以使用到消息队列,例如存在订单秒杀活动,我们可以通过异步的方式,先判断库存和是否是一人一单,校验成功后生成订单id放入消息队列中,直接返回,由消息消费者监听队列获取订单信息来进行对数据库的修改库存和生成订单操作,还有微服务的多个服务之中,可以使用消息队列来进行通信,而RabbitMQ是目前最流行的消息中间件之一,我们来简单的了解它。我们查看消息队列,发现存入的是字节,消息的可读性差,并且占据的空间极大,我们导入jackson的依赖,使其对消息进行转换。:生产者生产消息可以直接放入队列中。
2024-12-01 21:12:37
1302
原创 生成全局唯一ID
我们可以使用Long类型数据来作为订单id,Long类型是8个字节即64位,第一位代表符号,第二位到第三十二位可以存储时间戳,用来拼接保证订单id的安全性,第三十三位到六十四位可以存储序列号,序列号存储在redis的string类型中,一共可以生成2^32个订单号,为了防止订单过多存储不下并且可以统计每一天的订单,我们可以存储每一天的订单序列号在redis中。我们想要实现全局唯一ID并且实现自增,那么是不是需要定义一个变量,比如订单数量,每多一个订单该字段加一,然后将其保存到数据库中?
2024-11-30 17:44:14
575
原创 通过异步使用消息队列优化秒杀
在我们进行数据库的修改任务时我们使用了阻塞队列来实现,在实际的业务中,我们需要使用一些消息队列来代替阻塞队列,阻塞队列使用的是JVM中的内存,当消息过多时会造成JVM内存爆满,并且功能不够强大,我们可以使用Redis的Stream或者MQ来实现消息队列来代替阻塞队列。比如A来负责检验,检验成功后交给B来完成数据库的修改,这样是不是能够优化秒杀的业务。使用阻塞队列或者消息队列,为了实现简单,暂且使用阻塞队列,我们将要修改数据库的任务放入阻塞队列中,创建一个线程池,让线程池来负责执行修改数据库的任务。
2024-11-26 17:23:43
1412
原创 代理模式:静态代理和动态代理(JDK动态代理原理)
在以上的案例中,明星就是一个被代理类,而代理公司是一个代理类,明星的一些行为只能由公司来调用,而公司可以对明星的行为进行增强。动态代理即运行时动态的生成代理对象,和静态代理在编译时就确定代理对象不同,其灵活度更高,并且耦合度更低,动态代理又分为JDK动态代理和CGLIB动态代理。CGLIB动态代理是基于CGLIB工具类实现的,可以在运行时继承代理类根据代理类的类信息方法等生成代理类,因此被代理类不能被final修饰。我们在测试时是通过生成的代理类也就是上图的类来调用方法的,因此我们需要搞懂这个生成代理类。
2024-11-25 14:35:26
1179
原创 Redis进阶Redission实现分布式锁
我们可以将锁保存在Redis的Hash类型中,key用来存储唯一标识,value用来存储count,用来标识获取到锁的次数,每次想要获取到锁首先检验锁是否存在,如果存在检验key是否相等,如果相等说明是获取到锁的线程本身想要再次获取锁,就让count++,当释放锁的时候,让count–,当count为0时删除redis的锁。:当我们因为给锁添加了一个超时释放时间时,常常可能因为业务阻塞原因导致锁被超时释放,这是锁被其他线程获取,多个线程同时执行业务就可能会引发线程安全问题。TimeUnit:时间单位。
2024-11-23 21:04:24
696
原创 分布式锁的实现,基于Redis实现分布式锁
对于一些业务来说,比如商品或者优惠卷的抢杀,我们常常需要进行一些加锁的操作比如synchronized等来避免商品的超卖问题,但是对于大型的购物平台,都是使用了分布式的系统,将服务部署在多台服务器中,让请求通过轮询算法发送到多个服务器中,用来减少服务器的压力,但是使用了分布式系统也就会导致单机锁的失效,比如synchronized来讲,它是基于JVM的监视器锁,线程想要进入被其修饰的代码块就要获取JVM的监视器锁,但是对于多个服务器,它们有着不同的JVM,监视器锁也就不同,也就导致了多个服务器的线程可以同时
2024-11-22 21:40:15
1434
原创 Redis三剑客:缓存雪崩、缓存穿透、缓存击穿
我们不给键添加过期时间,让其一直存在缓存中,但是我们在将数据其添加进缓存中时,会给其添加一个逻辑过期的字段,当多线程到达时,我们首先命中缓存,取出逻辑过期的字段,判断数据是否过期,如果数据过期,让线程竞争锁,获取到锁的线程新创建一个线程来进行缓存重建的业务,然后主线程和其他未竞争到锁的线程直接返回过期的数据。缓存击穿产生原因:存在高并发的热点键,并且其重建缓存业务复杂,重建缓存耗时长,当热点键过期失效时,大量线程进入重建缓存,查询数据库,导致数据库压力暴涨。缺点:实现复杂,存在误差。1、基于互斥锁的实现。
2024-11-20 10:46:52
834
原创 Redis的过期删除策略和内存淘汰机制以及如何保证双写的一致性
线程A查询数据,如果此时由于缓存过期导致缓存失效,开始查询数据库,线程B相继更新数据库删除缓存,Time4线程A将查询出的旧数据放入缓存,导致缓存和数据库不一致。为了保证缓存和数据的一致性和节省缓存空间,就可以给存储的数据设置过期时间,而对于过期键的删除,redis以下三种策略,分别是定时删除、惰性删除、定期删除。按照以上的执行顺序,更新后的数据库是线程B更新的值,而缓存时线程A更新的值,此时数据库和缓存不一致,造成线程安全问题,因此避免使用更新策略。:从设置过期删除的键中,选择最近最少使用的键值删除。
2024-11-19 21:18:24
1168
原创 并发工具类:Semaphore、CountDownLatch、CyclicBarrier的作用以及用法
并发工具类:Semaphore、CountDownLatch、CyclicBarrier的作用以及用法
2024-09-24 20:08:13
1112
原创 死锁的产生原因以及解决方案
上图中线程一占有线程二所需的A资源,而线程二占有线程一所需的B资源,导致它们陷入了无限等待,我们使用jps命令和jstack查看线程状态。线程开始运行之后只有在运行完毕才会主动释放资源,不会被其他线程所剥夺。我们发现线程一和线程二都进入了BLOCKED状态,并且产生了死锁。死锁是由于多个线程相互占用各自所需的资源,导致它们无限等待。当线程因为获取不到资源被堵塞时,对以获得的资源保持不放。进一步申请所需资源,如果申请不到,释放已占用的资源。按序申请资源,按照反序释放资源。
2024-09-22 18:42:07
663
原创 为什么要使用线程池?线程池的创建、核心参数以及工作原理
服务器开发经常需要接收到客户端的任务,而这些任务可能需要极短时间就可以完成,如果一个任务需要创建一个线程,那么对于成千上万个简单任务,就需要创建上万个线程,显然这是不可能的,线程的创建和销毁都会浪费系统的性能。而线程池可以解决这个问题,我们预先创建几个线程,当任务到达时,使用已创建的线程不仅可以减少线程创建带来的消耗,还可以提高任务的响应效率,设置的最大线程数量以及线程的停留时间,还可以避免线程无限的创建。
2024-09-22 18:12:46
555
原创 Spring是如何解决Bean的循环依赖问题呢?
setter注入循环依赖指的是,类在进行实例化注册Bean之后,需要调用set方法进行依赖Bean的注入,A需要通过set方法注入B的依赖Bean,而B还没完全初始化,需要注入C的依赖Bean,C又要需要注入A的依赖Bean,就形成了一个循环,无法完成依赖注入。循环依赖就是循环引用,指的是两个或两个以上的Bean互相持有对方,形成循环,使得他们无法完成完全初始化。Spring通过三级缓存机制来解决单例Bean的setter注入循环依赖问题。循环依赖问题分为构造方法循环依赖和setter循环依赖。
2024-09-17 18:02:09
861
原创 面试必会2:HashMap、HashTable和ConcurrentHashMap的区别
面试必会2:HashMap、HashTable和ConcurrentHashMap的区别
2024-09-15 17:51:02
382
原创 对象的创建方式:浅拷贝和深拷贝
今天面试了一家公司,问到了一个面试题:对象的创建方式,我只想到了一个new关键字来创建,面试结束一查才恍然大悟,对象的创建方式不止是使用new来创建,还可以使用拷贝的方式,而拷贝又分为浅拷贝和深拷贝,我们来详细了解一下。
2024-09-14 22:02:32
1490
原创 百度二面算法手撕:将一个整数数组[a1,a2,a3,a4...]中的所有元素拼接起来,找到最大的值
将一个整数数组[a1,a2,a3,a4...]中的所有元素拼接起来,找到最大的值
2024-09-13 20:38:07
598
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅