消息队列选型
消息队列这是一篇对消息队列的详细总结(可能对于高可用和架构方面的总结还不够,未来会随着对知识理解的加深继续完善)。在项目中使用了 RabbitMQ,所以也记录了 RabbitMQ 的使用集成。 消息队列可以看成一个存放消息的容器,由于 Queue 是一种先进先出的数据结构,所以消息队列消费数据也是「按序」的。 消息队列基础消息队列有什么作用 这部分来自 JavaGuide 异步处理 将服务中一些「非核心的耗时业务」以异步方式执行,将消息加入消息队列中之后就立即返回结果,减少响应时间。 由于用户请求在将数据写入消息队列后就返回给用户,但是下游任务可能会因为各种情况而失败。所以在使用消息队列异步处理下游业务时还要进行一些流程上的修改——比如订机票、酒店时用户提交订单,后台将订单写入 MQ,不能立即返回订单提交成功,需要等待 MQ 中的订单消费者将订单出库后,再通过短信的方式通知用户。 为了保证消息队列中的消息被消费,会将消息缓存在生产者服务器的 buffer 中,直到收到消费者服务器的成功消费通知。 削峰 / 限流...
手写SpringBoot Starter
SpringBoot 自动装配用我的一句话总结自动装配就是:SpringBoot 通过起步依赖和自动装配机制来简化项目的构建和开发管理。 手动实现SpringBoot Starter步骤1 创建Maven项目创建新 Maven 项目,手动添加必要的 pom 依赖。 12345678910111213141516<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.7</version></parent><dependencies> <dependency> <groupId>org.springframework.boot</groupId> ...
MySQL是如何存储数据的
执行一条 select 语句,在 MySQL 中发生了什么? MySQL 数据文件存放在哪 InnoDB 数据组织方式InnoDB 的行记录作为 MySQL 用来存储数据的默认存储引擎 InnoDB,在内部到底是如何存储的呢?InnoDB 总共有四种数据记录的行格式,其中只需要知道 Compact 和 Dynamic,Compact 是一种紧凑的行格式,目的是能在数据页中尽可能多的存储更多记录,Dynamic 作为 5.7 版本之后默认的行格式是由 Compact 发展来的。 记录的额外信息 变长字段长度列表:先找出所有变长字段,对变长字段列逆序存放 NULL 值列表:先找出所有允许 null 的字段,用比特 0 表示不为 NULL,用比特 1 表示为 NULL,仍然对这些字段列逆序存放 为什么要对变长字段长度列表和 NULL 列表进行「逆序排列」?因为连接不同行记录的指针指向行记录的位置是记录头信息和真实数据之间的位置,这样的好处是向左可以读到记录的额外信息,向右可以直接读到真实数据。而逆序存放的目的是使位置靠前的记录的真实数据和数据对应的字段长度可以同时在一个...
垃圾收集器
JVM垃圾收集器JVM的垃圾回收机制一共分为两步:第一步先识别出不再使用的内存空间,第二步回收这些内存空间。 判断对象死亡的方式:1、引用计数法 → 无法解决对象之间循环引用2、可达性分析法 回收内存空间的方法:1、标记-清除算法 → 产生内存碎片2、复制算法(为了解决内存碎片问题:可用内存减少一半) → 对于存活时间久的对象需要频繁的复制,所以更适合存活时间短的新生代对象3、标记-整理算法(整理开销大) → 更适合不需要频繁GC的老年代4、分代收集算法:对堆内存分为新生代和老年代,应用不同的垃圾回收策略,提高回收效率。 介绍完这么多,终于可以引出今天的主题,也就是垃圾收集器。如果说垃圾回收算法是方法论,收集器就是依据方法论的具体实现。JVM垃圾收集器分为分代收集(CMS)和分区收集(G1、ZGC),下面就来一一介绍。 CMSCMS(Concurrent Mark...
springboot访问本地资源
SpringBoot映射本地图片,外界可通过网络路径发送 HTTP 请求访问该图片。 在工作中,我们会把图片保存到服务器本地的某个目录下,然后前端 image 标签的 src 属性,填写网络路径即可访问到该图片,这该如何配置呢?有两种方法: 第一种方法,我们可以修改 application.yml 文件 12345678spring: web: resources: # 支持本地图片上传之后的链接,其中 file:///d的用于win系统,后面的file: 适用于mac/linux系统 static-locations: - classpath:/static/ - file:///d:/tmp/storage/ - file:/tmp/storage/ 这个方法有个弊端,在某些场景下,可能会无效,比如你的项目中写了某些过滤器等原因。另外,使用yml配置,也会使你静态访问路径失效 第二种方法,添加 webConfig 配置类...
线程池
线程池的核心思想就是实现线程的复用,避免线程反复创建和销毁带来的性能开销。这里主要介绍了基本的 ThreadPoolExecutor 以及用于定时循环任务的 ScheduledThreadPoolExecutor ,而且线程池是面试的重点。 ThreadPoolExecutor线程池的核心思想就是实现线程的复用,避免线程反复创建和销毁带来的性能开销。 Java 通过 ThreadPoolExecutor 来创建线程池,其核心参数如下: 线程池 execute 工作 1public void execute(Runnable command){} 线程在线程池内部被封装成内部类 Worker 创建的对象。 在线程池的 execute 方法中是通过 addWorker 方法来创建线程并执行任务的,Worker 会将线程和任务一同封装到 Worker 对象中,在通过 runWorker 方法来执行任务。 在 runWorker 方法内部,通过 while 循环保证执行完任务的线程不被销毁,而是复用不断地 getTask...
通知等待Condition
ConditonObject 是 Condition 接口的实现类,用来做线程同步的通知等待,最重要的是它维护了一个单向的等待队列,要明确它和 AQS 队列的关系。此外还介绍了 LockSupport 类,Condition 的 await 底层就是通过 LockSupport 实现的。 等待通知 ConditionCondition 的等待队列是一个不带头节点的链式队列,而 AQS 的同步队列是带头节点的。 多个 Condition 对象的多个等待队列有什么用? 同步队列和等待队列的关系: 调用 Condition 的 await 方法,会使当前持有锁的线程进入同步队列,并释放锁,线程进入等待状态。相当于将同步队列的头节点(占用线程)移动到等待队列的尾部。 调用 Condition 的 signal 方法,唤醒该线程,相当于将等待队列的头节点移动到同步队列的尾部。 Condition 是个接口,它的实现类是 ConditionObject,是 AQS 的子类。 Condition 的 await 方法当前线程调用 Condtion.await()...
可重入锁 ReentrantLock、ReentrantReadWriteLock
可重入锁支持重入性,表示能够对共享资源重复加锁,即当前线程获取该锁后再次获取不会被阻塞,主要介绍了 ReentrantLock、ReentrantReadWriteLock 两种。 ReentrantLock支持重入性,表示能够对共享资源重复加锁,即当前线程获取该锁后再次获取不会被阻塞。 ReentrantLock 是通过抽象队列同步器 AQS 在 API 程序级别实现的,其内部类 Sync 继承了 AQS 抽象类,是 Lock 接口的默认实现。 源码 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功; 12345678910111213141516171819202122// 以内部类 Sync 的 nonfairTryAcquire 方法为例final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 1....
volatile和synchronized关键字
之所以把 volatile 和 synchronized 放在一起是因为二者都是关键字,而 synchronized 相比 volatile 除了提供内存可见性外还提供了锁的作用,也就是在同一时刻只允许一个线程访问共享资源,保证了线程安全性。 volatile关键字当使用 volatile 关键字修饰一个变量,Java 内存模型会插入一个内存屏障,确保执行到 volatile 变量的时候前面的语句全都执行完,后面的语句全都没有执行, 且前面的语句对 volatile 变量及后面的语句是可见的。 volatile 可以保证可见性,但不保证原子性: 当写一个 volatile 变量时,JMM 会把该线程在本地内存中的变量强制刷新到主内存中去; 这个写操作会导致其他线程中的 volatile 变量缓存无效(修改对其他线程立即可见)。 保证可见性: 1234567891011121314class ReorderExample { int a = 0; boolean volatile flag = false; public void writer()...
Java内存模型(JMM)
Java 内存模型(Java Memory Model,JMM)是个抽象的概念,负责 Java 并发中每个线程工作内存和共享主存之间的交互。JMM 很重要,也是后端程序员面试的重点。 Java 内存模型(Java Memory Model,JMM)是个抽象的概念,负责 Java 并发中每个线程工作内存和共享主存之间的交互。 JMM 定义了线程与主存之间的抽象关系: 线程间通信必须经过主存:① 线程 A 将本地内存 A 中更新过的共享变量刷新到主存中;② 线程 B 到主存中去读取线程 A 之前更新过的共享变量。 说到 JMM 正好复习一下跟它很像的 Java 运行时内存区域 Java 运行时内存区域描述的是在 JVM 运行时,如何具体地将内存划分为不同的区域: JMM 意在解决 Java 多线程并发编程中主内存和工作内存之间如何安全执行操作的问题: JMM 的指令重排 计算机在执行程序时为了性能,编译器和处理器往往会对指令进行重排序: 12a = b + c;d = e - f ; 加载完 b 和 c,可以一起把 e 和 f 都加载了,这样再去执行两个 add...