自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(34)
  • 收藏
  • 关注

原创 装饰器模式详解(附代码案例和源码分析)

通过多态,装饰器模式变得更加强大和灵活。它允许我们在运行时动态地组合不同的行为,同时保持代码的清晰和可维护性。这种组合为我们提供了一种优雅的方式来扩展对象的功能,而不需要创建大量的子类。

2025-01-16 10:17:12 1380

原创 底层分析为什么CAS不保证可见性

在CPU缓存架构,CAS只保证比较和交换这个操作是原子的,不保证值的可见性,每个CPU都有自己的缓存,CAS成功后值只更新到当前CPU的缓存中。

2025-01-09 15:59:17 937

原创 CAS操作的底层原理(总线锁定机制和缓存锁定机制 )

先总结一下,CAS是一种硬件级的原子操作,通过总线锁或缓存锁实现原子性,在使用缓存锁的时候缓存一致性协议保证数据一致性。而Java通过native方法调用底层CAS指令,需要考虑ABA问题和性能优化,而cas这种机制保证了在多处理器系统中的原子操作,是实现无锁并发的基础。

2025-01-09 15:32:20 1013

原创 RabbitMQ消费者重试的两种方案

将所有的消息都记录在数据库中进行保存,并以消息是否消费成功来更改数据库中消息的状态值,可以开启一个定时任务执行从数据库取出失败的消息进行重新消费,因为可能取出消费的时候还可能会失败,可以设置一次任务取出数据消费条数,若是超出条数则等到下次定时任务再进行消费。这样的方法其实和刚刚讲的原理差不多。复杂的业务处理,处理订单、支付等关键业务,需要完整的处理历史,可能需要人工介入。简单的消息处理,处理简单的、非关键的消息,失败影响较小,不需要追踪历史。一种是消息消费失败然后消费者直接重试,这需要配置消费者重试机制。

2025-01-09 14:01:50 940

原创 RabbitMQ解决消息积压的方法

这篇文章总结一下自己知道的解决消息挤压得方法。像我们没有必要知道一个的中间状态,只需知道一个最终状态就可以了。发送的消息体只用包含:id和状态等关键信息,不用发送一个完整的对象内容。消费者收到消息之后,通过id调用原服务再将完整的消息对象内容查询出来即可,最后再进行消费处理。采用动态增加消费者的数量批量消费消息临时队列转移监控和预警机制分阶段实施最后还有一个方法就是开启队列的懒加载

2025-01-08 17:12:23 1054

原创 ArrayList以及HashMap源码分析快速失败(fail-fast)机制

它实现了fail-safe机制,能够处理并发修改和动态扩容的情况。迭代器在创建时会记录当前的modCount值,在迭代过程中如果发现modCount发生变化,则说明集合被并发修改,立即抛出ConcurrentModificationException异常,这种机制可以帮助开发者及早发现并发修改问题,但不能完全保证线程安全。它在维护元素的插入顺序或访问顺序的同时,也保持了快速失败的特性,当检测到并发修改时会立即抛出异常,这对于需要保持元素顺序的场景特别重要,因为并发修改可能会破坏链表的正确性。

2025-01-04 00:17:41 1136

原创 Spring源码分析之事件机制——观察者模式(三)

用户注册时,UserService发布UserRegisteredEvent事件,EmailNotificationListener同步处理邮件发送,UserCacheListener同步更新缓存,AsyncAnalyticsListener异步处理统计数据,通过@Order注解控制监听器执行顺序。这种实现方式将用户注册的核心逻辑与后续的处理操作解耦,使得系统更容易维护和扩展。每个监听器都专注于自己的职责,可以独立地添加、移除或修改,而不会影响其他部分的代码。

2025-01-04 00:10:46 723

原创 Spring源码分析之事件机制——观察者模式(二)

这个方法负责检查监听器是否支持特定的事件类型,它考虑了泛型类型和特殊的监听器接口。整个实现展示了Spring如何高效地管理和匹配事件监听器,通过缓存机制提高性能,同时保证类型安全和正确的监听器顺序。这种实现既保证了功能的完整性,又确保了运行时的效率。这个方法是实际检索监听器的核心实现,它处理了已实例化的监听器和尚未实例化的监听器Bean,同时考虑了Bean的作用域和缓存策略。这个方法实现了监听器检索的缓存机制,通过缓存来提高性能,同时考虑了类加载器安全性。Spring容器启动时实例化Bean。

2025-01-03 17:37:42 756

原创 Spring源码分析之事件机制——观察者模式(一)

SimpleApplicationEventMulticaster提供了事件广播的具体实现,支持同步和异步两种事件处理方式,并提供了错误处理机制,通过这种设计,使得事件的处理更加灵活和可靠,同时也为性能优化提供了可能。

2025-01-03 17:07:22 742

原创 Jdk动态代理源码缓存优化比较(JDK17比JDK8)

总结实际应用影响高并发场景:更快的代理对象创建速度,避免额外的反射调用内存敏感场景:更少的内存占用,减少内存泄露的风险

2025-01-03 11:39:39 435

原创 JDK17源码分析Jdk动态代理底层原理

内存优化:单接口直接使用接口Class对象作为缓存key,而多接口需要克隆数组并转换为List,会产生额外的对象创建缓存key的复杂度:单接口的key就是一个Class对象,更简单高效,而多接口的key是List

2025-01-02 20:39:46 906

原创 JDK8源码分析Jdk动态代理底层原理

InvocationHandler最终存储在代理类实例的h字段中,这个字段是从Proxy类继承的。当调用代理对象的方法时,会调用这个h字段引用的InvocationHandler实例的invoke方法。3.ProxyClassFactory负责生成代理类的字节码,其中会,验证接口的合法性,生成唯一的代理类名,确定代理类的包名,调用ProxyGenerator生成字节码。这是整个代理创建过程的入口。这个方法会首先检查缓存中是否已经存在对应的代理类,如果不存在则创建新的代理类。生成的代理类大致结构。

2025-01-02 20:27:29 552

原创 FutureTask的底层源码解析

run方法是任务执行的核心逻辑。首先,它检查当前状态是否为NEW,并尝试将当前线程设置为运行者。如果状态不符合条件或设置失败,方法将直接返回。接下来,如果可调用的任务不为null且状态仍为NEW,则执行该任务并捕获可能的异常。如果任务成功执行,结果将被设置;如果发生异常,则会记录该异常。最后,在finally块中,清空runner以防止并发调用run方法,并重新检查状态以处理可能的取消中断。这种设计确保了任务的安全执行和异常处理。

2025-01-01 02:37:55 1021

原创 static 和final 关键字的作用

基本数据类型的变量被赋值之后的数据本身就不会再发生变化,引用数据类型的变量被赋值之后,引用的对象不会再发生变化,但对象内的属性可以发生变化。可变性:当一个变量被声明为 final 时,它的值在初始化后不能被改变。方法优化:当方法被声明为 final 时,子类不能重写这个方法,这使得编译器可以进行更好的内联优化,从而提高方法调用的效率。

2025-01-01 02:09:48 628

原创 Java代码实现优先级队列

最近看了PriorityQueue的源码实现后,我深有感悟,其实本质上就是用了堆的数据结构,我也自己尝试实现了优先级队列的代码,不多说了,代码如下。

2024-12-31 15:19:22 477

原创 LockSupport的源码实现原理(一)

在上可以看到c++源码实现的一个情况,获取到thread关联的parker对象,检查他的一个counter属性是不是大于0,大于0则已经有unpark了,直接返回。由源码中可以看出,LockSupport类有一个Unsafe对象的静态属性,通过Unsafe类的静态方法getUnsafe获取的,而在Unsafe类中就有个静态属性Unsafe,用的是单例模式饿汉式创建。可以看到实际上就是调用了unsafe类的park和unpark方法,至于这个blocker对象也是大有用处的,想要了解的读者可以看看这篇文章。

2024-12-31 14:32:58 579

原创 LockSupport底层源码分析(二)

在park方法需要传入一个对象,一般都会传入this对象,在park方法里会将这个this对象设置到Thread类的parkBlocker对象里,可用来监控和诊断,可以通过线程对象的parkBlocker对象判断该线程是由什么造成的一个阻塞。内存写入:将一个对象引用(arg)写入到线程对象(t)的特定内存偏移位(PARKBLOCKER),这是一个直接操作内存的操作,绕过了Java的访问控制。顺序性保证:写入操作不会与之前的写操作重排序,写入操作不会与之后的读操作重排序,维护程序的顺序性语义。

2024-12-31 14:31:28 488

原创 redission分布式锁中的公平锁、可重入锁、读写锁

读写锁利用的是reids的hash和string,hash(外面的大key是锁名称)里面的第一个键是mode,值是读写模式read或write,以后的键是锁标识UUID+threadId,值是重入的次数。执行加锁和释放锁的lua脚本的逻辑不一样。写锁是不会就超时记录的,因为写锁在同一时间只有一个线程能够获取(写写互斥),锁的超时时间就是线程持有锁的超时时间,所以不需要。公平锁靠的是hash存放持有锁的线程信息,list集合存放等待的线程,zset集合则是存放线程以及等待锁的时间。

2024-12-27 16:26:41 613

原创 微信朋友圈的设计实现

最近想了一想微信朋友圈的设计,突发想实现一下微信朋友圈的主要代码,代码有错还请指出

2024-12-27 16:14:29 262

原创 Rabbitmq源码分析,重复消费问题的redis或数据库代码实现

RabbitMQ判断重复消息主要通过消息的唯一标识(MessageId)和幂等性处理来实现我们该如何给消息添加唯一ID呢?其实很简单,SpringAMQP的MessageConverter自带了MessageID的功能,我们只要开启这个功能即可。

2024-12-26 18:01:08 1715

原创 synchronized锁升级机制底层原理

当一个线程第一次访问同步块时,如果同步对象没有被锁定,那么虚拟机会将对象头的标记位设置为"01",即偏向锁状态。同时,虚拟机会利用CAS操作在对象的Mark Word中记录下这个线程的ID。这样,当这个线程再次进入同步块时,虚拟机只需要检查对象头的MarkWord中是否存储着指向当前线程的偏向锁。如果是,则说明当前线程已经获得了锁,可以直接执行同步块的代码。这种情况下,不需要额外的同步操作,从而提高了程序的性能。

2024-12-26 17:21:34 880

原创 偏向锁详解,什么时候适合开启偏向锁?

批量重偏向(场景三)是一个相对轻量级的操作,只暂停必要的线程;而批量撤销(场景四)是一个重量级操作,需要暂停所有线程。在实际应用中,应该尽量避免频繁触发批量撤销,以免影响系统性能。

2024-12-26 15:04:54 1101

原创 Java中各种数组复制方式的效率对比

选择合适的方法:在选择数组复制方法时,应根据具体需求和性能要求进行选择。对于大规模数组复制,推荐使用 System.arraycopy;对于简单的数组复制,而手动复制适合需要复杂逻辑的场景。性能考虑:在性能敏感的应用中,尽量避免使用手动复制,优先考虑使用 System.arraycopy。

2024-12-25 21:54:50 329

原创 ThreadLocal源码详解

1.线程局部变量:ThreadLocal 提供了一种机制,使得每个线程可以独立地存储和访问变量,避免了线程间的干扰。2.实现机制:通过 ThreadLocalMap 存储每个线程的 ThreadLocal 变量,确保线程安全。3.父子线程关系:子线程不会继承父线程的 ThreadLocal 变量的值,但可以通过显式设置来使用,也可以通过inheritableThreadLocal。这种设计使得 ThreadLocal 在多线程编程中非常有用,尤其是在需要保持线程独立状态的场景中。

2024-12-25 21:37:09 901

原创 JDK8和JDK17的ArrayList 源码对比分析

JDK 8 和 JDK 17 的扩容机制并不是绝对的1.5倍扩容,两者都允许根据实际需要动态调整容量。JDK 17 的实现通过更灵活的扩容策略和更清晰的代码结构,提升了性能和可维护性,JDK 8扩容逻辑较为简单,分为几个步骤,JDK17的逻辑更为集中,减少了代码的复杂性。 个人去测试过JDK 8 和 JDK 17 中ArrayList添加数据的一个表现情况,JDK17中ArrayList的效率大概会比JDK 8有5%左右的提升。

2024-12-24 23:44:11 921

原创 Java中的五种引用方式底层详解

1. 强引用:最常用,不会被回收2.软引用:内存不足时回收3.弱引用:垃圾回收时回收4. 虚引用:随时可能被回收,必须配合引用队列使用5.终结器引用:用于实现对象的finalize方法这五种引用强度依次减弱:强引用 > 软引用 > 弱引用 > 虚引用/终结器引用缓存场景常用软引用防止内存泄漏常用弱引用(如WeakHashMap)跟踪对象回收状态用虚引用资源清理场景可能用到终结器引用我在第一次看代码的时候曾有个疑惑就是,为什么要用引用队列呢?不用引用队列不是一样可以回收吗?

2024-12-24 17:50:25 1027

原创 重写和重载的区别,以及运行时多态和编译时多态的区别

比如在这段代码中,调用的其实是Cat类对象的eat方法,但是编译完之后虚拟机指令中调用的是Animal类的eat方法,这就需要在运行过程中通过动态绑定找到Cat类的eat方法,这样就实现了多态。好了,现在重新回到重写的层面,子类重写了父类的方法,只有在运行时经过虚方法表这一系列的动态查找动作才能确定最后要执行的方法,才去创建对应的栈帧,调用具体方法的字节码,并不是在编译时就确定要调用哪个方法,所以重写才是运行时的多态性。方法调用的本质是通过字节码指令的执行,能在栈上创建栈帧,并执行调用方法中的字节码执行。

2024-12-23 17:48:30 1286

原创 explain估算rows不准确

MySQL的优化器在不同的版本中可能会有所不同。这可能导致同一个查询在不同版本的MySQL中执行计划不同。EXPLAIN 的结果是估算值,用于优化器选择执行计划实际执行可能与 EXPLAIN 的预估不同统计信息的准确性直接影响预估的准确性。

2024-12-23 01:46:11 216

原创 前端和后端限流策略的实现

最近想要到学校的官网上面查询一下学费缴纳的一个情况,然后进入页面的时候我就想着,学校的官网对应的接口应该也会有一定的限流算法,那他会采用什么样的限流算法呢?当我频繁快速点击学生缴费查看这个按钮来刷新页面时,发现页面就跳出了警告点击确认后就跳转到了登录界面此时我就想到了相应的算法,固定窗口算法?滑动窗口算法?漏桶算法?令牌桶算法?然后我就登录页面后继续实验,发现只要缓慢的点击刷新页面就不会跳出警告,也不会将我踢出登录。

2024-12-22 17:30:28 479

原创 Map大容量存储的话怎么解决扩容效率低的问题

在Map大容量存储的时候会有扩容效率低的问题,如亿级的map数据扩容,这就需要新创建一个map,还要对所有的数据进行rehash的操作,这就会造成扩容效率低的问题。

2024-12-19 17:53:45 404

原创 Redisson分布式锁为什么要设置锁释放时间

如果不设置过期时间,持有锁的服务器突然宕机或网络故障,没有来得及释放锁,这个锁将永远无法释放,其他服务无法获取到锁。JDK锁是单机锁,运行在同一个JVM中,进程崩溃时,JVM会自动释放锁资源,相对的那个看门狗机制设置的递归的定时任务也会释放销毁,redis中的锁也可以释放了。

2024-12-19 17:32:48 173

原创 PriorityQueue的扩容算法

PriorityQueue在Java中是一个基于堆的数据结构,用于存储优先级队列。其容量增长策略是为了在性能和内存使用之间取得平衡。

2024-12-19 17:07:58 157

原创 如果需要缓存的数据远超redis可用内存,怎么使得查询效果更好?

如果像有80G数据,但是redis只有15G,在这种需要缓存的数据远超redis可用内存的情况下怎么使得查询效果更好?

2024-12-19 16:39:48 194

原创 Hashmap怎么存储亿级数数据

分片存储(Sharding)内存优化

2024-12-19 16:26:59 339

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除