Java Hotspot 虚拟机中,每个对象都有对象头(包括 class 指针和 Mark Word)。Mark Word 平时存储这个对象的 哈希码、分代年龄,当加锁时,这些信息就根据情况被替换为 标记位(轻重量级锁)、线程锁记录指针、重量级锁指针、线程ID等内容。
Mark Word:8位。加锁替换后,把之前的暂存到栈针中。
| 使用场景 | 锁类型 |
|---|---|
| 多个线程交替执行 | 轻量级锁 |
| 编号 | 锁类型 |
|---|---|
| 00 | 轻量锁 |
| 01 | 无锁 |
| 10 | 重量锁 |

1 轻量级锁
交替级使用
2、锁膨胀
交替级使用
3、线程状态
线程阻塞:
线程阻塞,保存当前状态,需要经过唤醒才能继续使用。
线程自旋重试:
智能化自旋尝试,自旋时间不固定,会基于上次成功的时间进行判断。线程不会停止,多核CPU进行获取锁机制。
会产生死循环的问题
在1.8中,引入了红黑树优化数组链表,同时改成了尾插,按理来说是不会有环了,但是还是会出现死循环的问题,在链表转换成红黑数的时候无法跳出等多个地方都会出现这个问题。
put数据丢失描述
在下方代码注释处,线程已经拿到了头结点和hash桶,若此时cpu挂起,重新进入执行前,这个hash桶已经被其他线程更改过,那么在该线程重入后,他将持有一个过期的桶和头结点,并且覆盖之前其他线程的记录,造成了数据丢失。
容量size的不准确
size只是用了transient(不参与序列化)关键字修饰,在各个线程中的size不会及时同步,在多个线程操作的时候,size将会被覆盖。
线程池创建方式

// 可缓存线程池
ExecutorService executorService1 = Executors.newCachedThreadPool();
// 单线程线程池
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
// 固定数量线程池
ExecutorService executorService3 = Executors.newFixedThreadPool(1);
// 并行线程池
ExecutorService executorService4 = Executors.newWorkStealingPool(3);
// 单线程线程池
ScheduledExecutorService scheduledExecutor1 = Executors.newSingleThreadScheduledExecutor();
// 延迟线程池
ScheduledExecutorService scheduledExecutor2 = Executors.newScheduledThreadPool(1);
阿里巴巴不推荐的4种线程池创建方式
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
// 可缓存线程池
ExecutorService executorService1 = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
// 单线程线程池
ExecutorService executorService2 = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
// 固定数量线程池
ExecutorService executorService3 = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
// 并行线程池
ExecutorService executorService4 = new ForkJoinPool(3, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
| 与不同引擎的关系 | 核心作用 | 生命周期 | 日志类型 | ||
|---|---|---|---|---|---|
| undo log | 属于innodb引擎独有 | 回滚,保证事务的“原子性”,事务日志 | 事务开始前,以类似“快照”的方式记录现场 | 逻辑日志 | 逻辑日志:记录语句原始逻辑(SQL)。 |
| redo log | 属于innodb引擎独有 | 重做,保证事务的“持久性”,事务日志 | 事务开始后记录,prepare阶段落盘 | 物理日志 | 物理日志:记录在某个页面上做了什么修改 |
| binlog | 工作在mysql的Server层,与使用哪种引擎无关 | 实现主从节点数据的复制 | 事务执行期间记录,commit阶段完成前落盘 | 逻辑日志 |
Multi-Version Concurrency Control 多版本并发控制
即基于非主键索引的查询需要多扫描一棵索引树。
回表,顾名思义就是回到表中,也就是先通过普通索引扫描出数据所在的行,再通过行主键ID 取出索引中未包含的数据。所以回表的产生也是需要一定条件的,如果一次索引查询就能获得所有的select 记录就不需要回表,如果select 所需获得列中有其他的非索引列,就会发生回表动作。即基于非主键索引的查询需要多扫描一棵索引树。

数据量过大:
会查询时间太长,内存消耗过大。
解决办法:
大key的风险:
读写大key会导致超时严重,甚至阻塞服务。
如果删除大key,DEL命令可能阻塞Redis进程数十秒,使得其他请求阻塞,对应用程序和Redis集群可用性造成严重的影响。
redis使用会出现大key的场景:
单个简单key的存储的value过大;
hash、set、zset、list中存储过多的元素。
解决问题:
1.单个简单key的存储的value过大的解决方案:
将大key拆分成对个key-value,使用multiGet方法获得值,这样的拆分主要是为了减少单台操作的压力,而是将压力平摊到集群各个实例中,降低单台机器的IO操作。
2.hash、set、zset、list中存储过多的元素的解决方案:
1).类似于第一种场景,使用第一种方案拆分;
2).以hash为例,将原先的hget、hset方法改成(加入固定一个hash桶的数量为10000),先计算field的hash值模取10000,确定该field在哪一个key上。
将大key进行分割,为了均匀分割,可以对field进行hash并通过质数N取余,将余数加到key上面,我们取质数N为997。
那么新的key则可以设置为:
newKey = order_20200102_String.valueOf( Math.abs(order_id.hashcode() % 997) )
field = order_id
value = 10
hset (newKey, field, value) ;
hget(newKey, field)
跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
加来一层索引后,查找一个节点需要遍历的节点个数减少了,也就是说查询效率得到了提升,同理我们在一级索引的基础上,在加二级索引。
实现结构:
Redis的跳跃表由zskiplistNode和zskiplist两个结构定义。
zskiplistNode:其中zskiplistNode结构用于表示跳跃表节点。zskiplist: 结构则用于保存跳跃表节点的相关信息。zskiplist结构:
header:指向跳跃表的表头节点,通过这个指针程序定位表头节点的时间复杂度就为O(1)。tail:指向跳跃表的表尾节点,通过这个指针程序定位表尾节点的时间复杂度就为O(1)。level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内);通过这个属性可以再O(1)的时间复杂度内获取层高最高的节点的层数。length:记录跳跃表的长度,也即是,跳跃表目前包含节点的数量(表头节点不计算在内)通过这个属性,程序可以再O(1)的时间复杂度内返回跳跃表的长度。https://blog.csdn.net/sssxlxwbwz/article/details/123769262
1. 设置Key
2. 看门口机制
// 注册到容器(@Configuration)
@SpringbootConfiguration
// 自动配置
@EnableAutoConfiguration
// 包扫描
@ComponentScan
**Consistency(一致性):**用户访问分布式系统中的任意节点,得到的数据必须一致。
Availability (可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝。
Partition(分区):因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。
Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务。
案例:
ES集群:是低可用性,高一致性。属于CP。
Nacos 集群:默认是AP模式。即虽然有主从节点之分,但是在恢复模式 选举过程中仍可对外提供服务,不影响新服务注册。因节点宕机而丢失的注册信息会在节点重启后恢复。
生产端
消费端
enable.auto.commit 为 false,同时使用同步提交及在代码中使用commitOffsetsSync 函数按照 offset 的维度进行消息提交。服务端
log.flush.interval.messages参数为 1,也就是每写入一条消息就强制刷盘。默认情况下 kafka 是不控制刷盘的,交给 OS 去控制。https://blog.csdn.net/qq_38571987/article/details/118492557
1、顺序写入磁盘,增加IO性能
采用顺序写入磁盘的方式:顺序写入磁盘的速度是要快于随机写入内存的。
Kafka就是采用了顺序写入的方式,每次新的内容写入都是采用文件追加的方式,这也就以为着每次新写入的数据都是在文件的结尾,并且对于之前已经写入的内容是不能够进行修改的。
2、页缓存pageCache
Kafka会将一些热点数据放在PageCache中。(热点数据:最近访问的数据)
3、零拷贝
因为Kafka的消息是存在磁盘的,消息是在生产者,Broker,消费者中进行网络传输的,这里就涉及到了消息在磁盘到网络的转换。而零拷贝的作用就是通过减少用户态和内核态的转换,从而减少消息在磁盘到网络传输的资源损耗。
4、网络数据采用压缩算法
在Kafka中消息是在生产者,Broker,消费者进行传输的,Kafka采用的数据压缩的方式,以时间换空间,通过cpu时间的增加来尽量的减少磁盘空间的占用和网络IO的传输量,Kafka中消息的压缩是发生在生产者和Broker端的。
SpringIOC:
工程模式、单例模式、装饰者模式
Spring AOP:
代理模式、观察者模式
Spring MVC:
委派模式、适配器模式
Spring JDBC:
模板方法模式
懒汉式
public class Signletion {
private static Signletion signletion = null;
private Signletion() {
}
public static Signletion getSignletion() {
if (signletion == null) {
synchronized (Signletion.class) {
if (signletion == null) {
signletion = new Signletion();
}
}
}
return signletion;
}
}
饿汉式
public class Signletion {
private static final Signletion signletion = new Signletion();
private Signletion() {
}
public static Signletion getSignletion() {
return signletion;
}
}
创建步骤
代理的调用
Elasticsearch 是一个高度可用的分布式搜索引擎。每个索引都分解为分片(shard),每个分片可以有一个或多个副本。默认情况下,创建一个索引,每个分片有1个分片和1个副本(1/1)。可以使用许多拓扑,包括1/10(提高搜索性能,多个副本可以帮我们提高搜索的速度)或20/1(提高索引性能,多个主分片可以帮我们提高导入数据的速度)。
为了使用 Elasticsearch 的分布式特性,只需启动更多节点并关闭节点。系统将继续为索引的最新数据提供请求(确保使用正确的 HTTP 端口)。

每个线程操作数据的时候会把数据从主内存读取到⾃⼰的⼯作内存,如果他操作了数据并且写会了,他其他已经读取的线程的变量副本就会失效了,需要都数据进⾏操作⼜要再次去主内存中读取了。
volatile保证不同线程对共享变量操作的可⻅性,也就是说⼀个线程修改了volatile修饰的变量,当修改写回主内存时,另外⼀个线程⽴即看到最新的值。
缺点:
由于Volatile的MESI缓存⼀致性协议,需要不断的从主内存嗅探和cas不断循环,⽆效交互会导致总线带宽达到峰值。
MESI(缓存⼀致性协议)
当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存⾏置为⽆效状态,因此当其他CPU需要读取这个变量时,发现⾃⼰缓存中缓存该变量的缓存⾏是⽆效的,那么它就会从内存重新读取。
总结:
volatile只能修饰实例变量和类变量,⽽synchronized可以修饰⽅法,以及代码块。
volatile保证数据的可⻅性,但是不保证原⼦性(多线程进⾏写操作,不保证线程安全);⽽synchronized是⼀种排他(互斥)的机制。 volatile⽤于禁⽌指令重排序:可以解决单例双重检查对象初始化代码执⾏乱序问题。
volatile可以看做是轻量版的synchronized,volatile不保证原⼦性,但是如果是对⼀个共享变量进⾏多个线程的赋值,⽽没有其他的操作,那么就可以⽤volatile来代替synchronized,因为赋值本身是有原⼦性的,⽽volatile⼜保证了可⻅性,所以就可以保证线程安全了。
Long.Max_Value-timestamp追加到key的末尾。例如 [key][reverse_timestamp] , [key]的最新值可以通过scan [key]获得[key]的第一条记录,因为 HBase 中 RowKey 是有序的,第一条记录是最后录入的数据。CAS(compare and swap):CPU一条并发原句。比较并交换。
判断内存中,某个位置是否为预期值。
存在于sun.misc.Unsafe中方法。
缺点
循环时间长,开销大。
只能保证1个共享变量的原子操作。
spring boot中的start装载上去的。
实现MyBatis的自动。
谁去解析xml的。
组装之前的操作,标签的解析。
进程线程区别。
异常回滚。