Skip to content

Commit

Permalink
文章更新
Browse files Browse the repository at this point in the history
  • Loading branch information
lizhifuabc committed May 13, 2024
1 parent 02792fe commit 028bc97
Show file tree
Hide file tree
Showing 24 changed files with 348 additions and 9 deletions.
4 changes: 4 additions & 0 deletions docs/Interview/2024年05月08日.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@

- 线程池的核心参数有哪些,如果提交一个任务到线程池,创建的线程数是多少,如何保证线程池的线程进行存活的机制,结合源码讲一下

- corePoolSize + allowCoreThreadTimeOut + keepAliveTime、workQueue、maximumPoolSize、RejectedExecutionHandler、threadFactory

- 当一个线程处理完当前任务后,就会开始去阻塞队列获取任务,只不过,在调用poll或take方法之前,会判断当前线程池中有多少个线程,如果多于核心线程数,那么当前线程机会调用poll()并设置超时时间来获取阻塞队列中的任务,这样一旦时间到了还没有获取到任务,那么线程就不会阻塞了,并且没有业务执行,那么线程就会运行结束,也就是回收了。

- MVCC机制实现原理,数据结构以及实现

mysql表中每行列都有2个隐藏列 分别保存了 对应的事务id和redo log日志的索引。每当对数据进行修改时 修改后redo log日志的索引会指向老的数据。这样才形成了 redo日志的版本链, 版本链和readview 才实现了mvcc。才实现了mysql的可重复读和读已提交2个事务隔离级别。readview的话 有几个重要的结构:当前事务、事务id数组(包含目前活跃的事务id)最小事务id、最大事务id。然后还要说清楚readview的运行逻辑。 其实这个还是八股 就是之前我提到的 mysql的ACID是如何实现的中的事务相关内容。可以多背背
Expand Down
10 changes: 10 additions & 0 deletions docs/Interview/2024年05月10日.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,14 @@

- HashMap 的经典设计有什么

- jdk 线程池与 tomcat 线程池对比

对比 Tomcat 线程池和 JDK 线程池,一个是线程数未达到最大线程数之前,优先创建线程执行任务,另一个是队列未满,优先让任务排队,总体而言tomcat线程池更适用于 IO 密集型应用场景,而对于CPU密集型任务,`ThreadPoolExecutor `是更通用和灵活性更高一些

- 线程池原理

线程池做的工作主要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。

他的主要特点为:线程复用、控制最大并发数、管理线程。


15 changes: 15 additions & 0 deletions docs/Interview/2024年05月12日.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 2024年05月12日

- Spring Bean 注册

- Spring 三级缓存

- 一级缓存这样存放成品 Bean,不能解决循环依赖的问题的。因为 A 的成品创建依赖于 B,B的成品创建又依赖于 A,当需要补全B的属性时 A 还是没有创建完,所以会出现死循环。

- 二级缓存用于存放半成品对象,继续创建,创建的半成品同样放到缓存中。
- 三级缓存主要是解决 Spring AOP 的特性。AOP 本身就是对方法的增强,是 `ObjectFactory<?>` 类型的 lambda 表达式,而 Spring 的原则又不希望将此类类型的 Bean 前置创建,所以要存放到三级缓存中处理。

- 聊一下 ThreadLocal

- `ThreadLocal` 底层采用的是数组结构存储数据。
- `new ThreadLocal<>().remove();` 操作。避免弱引用发生GC后,导致内存泄漏的问题。
4 changes: 3 additions & 1 deletion docs/Interview/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@

[2024年05月08日](./2024年05月08日.md)

[2024年05月10日](./2024年05月10日.md)
[2024年05月10日](./2024年05月10日.md)

[2024年05月12日](./2024年05月12日.md)
36 changes: 28 additions & 8 deletions docs/database/nosql/缓存和数据库的一致性问题.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 缓存和数据库的一致性问题

数据库和缓存(redis)双写数据一致性问题,在高并发的场景下,这个问题尤为严重。
从本质上讲,无论是先写数据库还是先写缓存,都是为了保证数据库和缓存的数据一致。数据库和缓存(redis)双写数据一致性问题,在高并发的场景下,这个问题尤为严重。

首先:只要是双写,总会有数据一致性的问题,大多数情况下我们并不是严格要求**缓存+数据库** 必须保持一致性。

Expand Down Expand Up @@ -42,25 +42,45 @@
3. 请求1:步骤2此时才开始执行。
4. 请求3:获取缓存内的数据,此时获取到的是请求1写入缓存的数据,此时读取的是旧数据。

### 先更新数据库,再删除缓存

先更新数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。

![image-20230910171256573](image/image-20230910171256573.png)

### 先删除缓存,再更新数据库

- 数据库更新失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致
- 读的时候缓存没有,所以去读了数据库中的旧数据,然后更新到缓存中。

![image-20230910171317000](image/image-20230910171317000.png)

### 延时双删
### 推荐:先更新数据库,再删除缓存(删除重试)

先更新数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。

![image-20230910171256573](image/image-20230910171256573.png)

> 此时可以添加重试删除操作,直到成功(对业务线代码造成大量的侵入)。
### 推荐:先更新数据库,再删除缓存(延时双删)

依旧是**先更新数据库,再删除缓存**,一段时间间隔后再一次删除缓存。

![image-20230910171339495](image/image-20230910171339495.png)

方案一:

1. 更新数据库数据

2. 数据库将数据表数据的变更信息写入binlog日志当中

3. 订阅程序获取所需要的数据以及key

4. 程序逻辑中处理具体的业务逻辑,接收订阅binlog、发起删除缓存的请求。

5. 尝试删除缓存操作,发现删除失败

6. 将这些信息发送至消息队列

7. 重新从消息队列中获得该数据,重试操作。

其他:

- 使用 `DelayQueue`,会随着 JVM 进程的死亡,丢失更新的风险
- 使用 `MQ`
- 缓存设置有效期
Expand Down
8 changes: 8 additions & 0 deletions docs/java/concurrent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@
[CompletableFuture](./CompletableFuture.md)

[ConcurrentHashMap](./ConcurrentHashMap.md)



线程池:

[线程池](./线程池.md)

[四种线程池](./四种线程池.md)
Binary file added docs/java/concurrent/image/0jgaovcjl8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/30dkd7bmt0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/8g7sroglys.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/ezyra2a98g.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/gcn3uiw2wg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/hppq1bllq4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/iq2cn7c9eu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/ja4c974izo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/mn820rtb6b.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/pihw1794iu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/u5x078fkiq.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/java/concurrent/image/zedywpyew6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions docs/java/concurrent/四种线程池.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# 四种线程池

Executors 是创建线程池的工具类,比较典型常见的四种线程池包括:newFixedThreadPool 、 newSingleThreadExecutor 、 newCachedThreadPool 、newScheduledThreadPool。

## newFixedThreadPool

- 固定大小可重复使用的线程池,corePoolSize = maximumPoolSize
- 以 LinkedBlockingQueue 无界阻塞队列存放等待线程。
- 随着线程任务不能被执行的的无限堆积,可能会导致 OOM

```java
public static ExecutorService newFixedThreadPool(int nThreads,
ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
```

![image-20240512112625906](./image/image-20240512112625906.png)

## newSingleThreadExecutor

- 只创建一个执行线程任务的线程池
- 如果出现意外终止则再创建一个
- 个无界队列存放待执行线程,无限堆积下会出现 OOM

```java
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
```

## newCachedThreadPool

- SynchronousQueue 是一个生产消费模式的阻塞任务队列,只要有任务就需要有线程执行,线程池中的线程可以重复使用。
- 如果线程任务比较耗时,又大量创建,会导致 OOM。

```java
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
```

## newScheduledThreadPool

- 延迟定时执行,有点像我们的定时任务
- 无限大小的线程池 Integer.MAX_VALUE
- 无限容量的线程池,所以依旧有 OOM 风险

```java
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
```

## 总结

![img](./image/gcn3uiw2wg-20240512113446077.png)
85 changes: 85 additions & 0 deletions docs/java/concurrent/线程池.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# 线程池

为什么使用线程池:线程池的核心目的就是资源的利用,避免重复创建线程带来的资源消耗。因此引入一个池化技术的思想,避免重复创建、销毁带来的性能开销。

- 当需要多线程并发执行任务时,只能不断的通过new Thread创建线程,每创建一个线程都需要在堆上分配内存空间,同时需要分配虚拟机栈、本地方法栈、程序计数器等线程私有的内存空间,当这个线程对象被可达性分析算法标记为不可用时被GC回收,这样频繁的创建和回收需要大量的额外开销。
- JVM的内存资源是有限的,如果系统中大量的创建线程对象,JVM很可能直接抛出OutOfMemoryError异常,还有大量的线程去竞争CPU会产生其他的性能开销,更多的线程反而会降低性能,所以必须要限制线程数。

线程池好处:

- 线程池可以复用池中的线程,不需要每次都创建新线程,减少创建和销毁线程的开销;
- 线程池具有队列缓冲策略、拒绝机制和动态管理线程个数,特定的线程池还具有定时执行、周期执行功能
- 线程池可实现线程环境的隔离

## 执行流程



## 构造方法

![img](./image/mn820rtb6b.png)

![img](./image/gcn3uiw2wg.png)

## 核心参数

![img](./image/iq2cn7c9eu.png)

- corePoolSize(int):核心线程数量。
- 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到任务队列当中。线程池将长期保证这些线程处于存活状态,即使线程已经处于闲置状态。除非配置了allowCoreThreadTimeOut=true,核心线程数的线程也将不再保证长期存活于线程池内,在空闲时间超过keepAliveTime后被销毁。
- workQueue:阻塞队列,存放等待执行的任务,线程从workQueue中取任务,若无任务将阻塞等待。当线程池中线程数量达到corePoolSize后,就会把新任务放到该队列当中。JDK提供了四个可直接使用的队列实现,分别是:基于数组的有界队列ArrayBlockingQueue、基于链表的无界队列LinkedBlockingQueue、只有一个元素的同步队列SynchronousQueue、优先级队列PriorityBlockingQueue。在实际使用时一定要设置队列长度。
- maximumPoolSize(int):线程池内的最大线程数量,线程池内维护的线程不得超过该数量,大于核心线程数量小于最大线程数量的线程将在空闲时间超过keepAliveTime后被销毁。当阻塞队列存满后,将会创建新线程执行任务,线程的数量不会大于maximumPoolSize。
- keepAliveTime(long):线程存活时间,若线程数超过了corePoolSize,线程闲置时间超过了存活时间,该线程将被销毁。除非配置了allowCoreThreadTimeOut=true,核心线程数的线程也将不再保证长期存活于线程池内,在空闲时间超过keepAliveTime后被销毁。
- TimeUnit unit:线程存活时间的单位,例如TimeUnit.SECONDS表示秒。
- RejectedExecutionHandler:拒绝策略,当任务队列存满并且线程池个数达到maximunPoolSize后采取的策略。ThreadPoolExecutor中提供了四种拒绝策略,分别是:抛RejectedExecutionException异常的AbortPolicy(如果不指定的默认策略)、使用调用者所在线程来运行任务CallerRunsPolicy、丢弃一个等待执行的任务,然后尝试执行当前任务DiscardOldestPolicy、不动声色的丢弃并且不抛异常DiscardPolicy。项目中如果为了更多的用户体验,可以自定义拒绝策略。
- threadFactory:创建线程的工厂,虽说JDK提供了线程工厂的默认实现DefaultThreadFactory,但还是建议自定义实现最好,这样可以自定义线程创建的过程,例如线程分组、自定义线程名称等。

## 提交线程 execute

通过execute方法提交任务时,当线程池中的线程数小于corePoolSize时,新提交的任务将通过创建一个新线程来执行,即使此时线程池中存在空闲线程。

![img](./image/u5x078fkiq.png)

当线程池中线程数量达到corePoolSize时,新提交的任务将被放入workQueue中,等待线程池中线程调度执行。

![img](./image/ja4c974izo.png)

当workQueue已存满,且maximumPoolSize大于corePoolSize时,新提交的任务将通过创建新线程执行。

![img](./image/30dkd7bmt0.png)

当线程池中的线程执行完任务空闲时,会尝试从workQueue中取头结点任务执行。

![img](./image/8g7sroglys.png)



当线程池中线程数达到maxmumPoolSize,并且workQueue也存满时,新提交的任务由RejectedExecutionHandler执行拒绝操作。

![img](./image/pihw1794iu.png)

当线程池中线程数超过corePoolSize,并且未配置allowCoreThreadTimeOut=true,空闲时间超过keepAliveTime的线程会被销毁,保持线程池中线程数为corePoolSize。

![img](./image/0jgaovcjl8.png)

当设置allowCoreThreadTimeOut=true时,任何空闲时间超过keepAliveTime的线程都会被销毁。

![img](./image/hppq1bllq4.png)

## 线程池底层实现原理

![img](./image/zedywpyew6.png)

线程池不同状态之间的转换时机及转换关系

![img](./image/ezyra2a98g.png)









> https://www.cnblogs.com/JavaYuYin/p/18012766
2 changes: 2 additions & 0 deletions docs/spring/springCloud/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Spring Cloud

[Spring Cloud LoadBalancer 负载均衡策略与缓存机制](./SpringCloudLoadBalancer负载均衡策略与缓存机制.md)

[Resilience4j是一个轻量级、高性能并易用容错框架](./Resilience4j.md)

[Resilience4j整合案例](./Resilience4j整合案例.md)
Expand Down
Loading

0 comments on commit 028bc97

Please sign in to comment.