1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-08-01 16:28:03 +08:00

Compare commits

...

7 Commits

Author SHA1 Message Date
Guide
cbac102d92
Merge pull request #2383 from TedLau/patch-3
Update distributed-lock-implementations.md
2024-05-01 16:21:51 +08:00
TedLau
a0546281de
Update distributed-lock-implementations.md
标示似乎应该是标识
2024-05-01 13:53:12 +08:00
Guide
52cce06e41 [docs update]补充完善消息队列应用场景&spi机制 2024-04-30 10:02:33 +08:00
Guide
c9746ff2df
Merge pull request #2379 from mangyimang/main
Update spi.md
2024-04-29 17:45:29 +08:00
Guide
c1d2b32359
Merge pull request #2381 from haorenlin/main
Update io-model.md
2024-04-29 17:44:55 +08:00
yanjunlin
18d066aa21
Update io-model.md
#0000 单词补全 java-io-模型详解
2024-04-29 16:13:16 +08:00
mangyimang
ec5e3e7923
Update spi.md
问题来源:https://github.com/Snailclimb/JavaGuide/issues/2378
对于以上问题所指出的SPI机制的设计和使用场景的修正阐述
2024-04-25 16:23:39 +08:00
6 changed files with 36 additions and 14 deletions

View File

@ -553,15 +553,19 @@ Redis 通过 **IO 多路复用程序** 来监听来自客户端的大量连接
### Redis6.0 之前为什么不使用多线程?
虽然说 Redis 是单线程模型,但是,实际上,**Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。**
虽然说 Redis 是单线程模型,但实际上,**Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。**
不过Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主线程之外的其他线程来“异步处理”。
不过Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主线程之外的其他线程来“异步处理”,从而减少对主线程的影响
为此Redis 4.0 之后新增了`UNLINK`(可以看作是 `DEL` 的异步版本)、`FLUSHALL ASYNC`(清空所有数据库的所有 key不仅仅是当前 `SELECT` 的数据库)、`FLUSHDB ASYNC`(清空当前 `SELECT` 数据库中的所有 key等异步命令。
为此Redis 4.0 之后新增了几个异步命令:
- `UNLINK`:可以看作是 `DEL` 命令的异步版本。
- `FLUSHALL ASYNC`:用于清空所有数据库的所有键,不限于当前 `SELECT` 的数据库。
- `FLUSHDB ASYNC`:用于清空当前 `SELECT` 数据库中的所有键。
![redis4.0 more thread](https://oss.javaguide.cn/github/javaguide/database/redis/redis4.0-more-thread.png)
大体上来说Redis 6.0 之前主要还是单线程处理。
总的来说,直到 Redis 6.0 之前Redis 的主要操作仍然是单线程处理的
**那 Redis6.0 之前为什么不使用多线程?** 我觉得主要原因有 3 点:

View File

@ -56,7 +56,7 @@ OK
```
- **lockKey**:加锁的锁名;
- **uniqueValue**:能够唯一标锁的随机字符串;
- **uniqueValue**:能够唯一标锁的随机字符串;
- **NX**:只有当 lockKey 对应的 key 值不存在的时候才能 SET 成功;
- **EX**过期时间设置秒为单位EX 3 标示这个锁有一个 3 秒的自动过期时间。与 EX 对应的是 PX毫秒为单位这两个都是过期时间设置。

View File

@ -41,15 +41,17 @@ tag:
## 消息队列有什么用?
通常来说,使用消息队列能为我们的系统带来下面三点好处:
通常来说,使用消息队列主要能为我们的系统带来下面三点好处:
1. **通过异步处理提高系统性能(减少响应所需时间)**
2. **削峰/限流**
3. **降低系统耦合性。**
1. 通过异步处理提高系统性能(减少响应所需时间)
2. 削峰/限流
3. 降低系统耦合性
除了这三点之外,消息队列还有其他的一些应用场景,例如实现分布式事务、顺序保证和数据流处理。
如果在面试的时候你被面试官问到这个问题的话,一般情况是你在你的简历上涉及到消息队列这方面的内容,这个时候推荐你结合你自己的项目来回答。
### 通过异步处理提高系统性能(减少响应所需时间)
### 通过异步处理提高系统性能(减少响应时间)
![通过异步处理提高系统性能](https://oss.javaguide.cn/github/javaguide/Asynchronous-message-queue.png)
@ -93,6 +95,18 @@ RocketMQ、 Kafka、Pulsar、QMQ 都提供了事务相关的功能。事务允
![分布式事务详解 - MQ事务](https://oss.javaguide.cn/github/javaguide/csdn/07b338324a7d8894b8aef4b659b76d92.png)
### 顺序保证
在很多应用场景中,处理数据的顺序至关重要。消息队列保证数据按照特定的顺序被处理,适用于那些对数据顺序有严格要求的场景。大部分消息队列,例如 RocketMQ、RabbitMQ、Pulsar、Kafka都支持顺序消息。
### 延时/定时处理
消息发送后不会立即被消费,而是指定一个时间,到时间后再消费。大部分消息队列,例如 RocketMQ、RabbitMQ、Pulsar、Kafka都支持定时/延时消息。
### 数据流处理
针对分布式系统产生的海量数据流,如业务日志、监控数据、用户行为等,消息队列可以实时或批量收集这些数据,并将其导入到大数据处理引擎中,实现高效的数据流管理和处理。
## 使用消息队列会带来哪些问题?
- **系统可用性降低:** 系统可用性在某种程度上降低,为什么这样说呢?在加入 MQ 之前,你不用考虑消息丢失或者说 MQ 挂掉等等的情况,但是,引入 MQ 之后你就需要去考虑了!

View File

@ -14,9 +14,9 @@ head:
> 本文来自 [Kingshion](https://github.com/jjx0708) 投稿。欢迎更多朋友参与到 JavaGuide 的维护工作,这是一件非常有意义的事情。详细信息请看:[JavaGuide 贡献指南](https://javaguide.cn/javaguide/contribution-guideline.html) 。
在面向对象的设计原则中,一般推荐模块之间基于接口编程,通常情况下调用方模块是不会感知到被调用方模块的内部具体实现。一旦代码里面涉及具体实现类,就违反了开闭原则。如果需要替换一种实现,就需要修改代码
面向对象设计鼓励模块间基于接口而非具体实现编程以降低模块间的耦合遵循依赖倒置原则并支持开闭原则对扩展开放对修改封闭。然而直接依赖具体实现会导致在替换实现时需要修改代码违背了开闭原则。为了解决这个问题SPI 应运而生它提供了一种服务发现机制允许在程序外部动态指定具体实现。这与控制反转IoC的思想相似将组件装配的控制权移交给了程序之外
为了实现在模块装配的时候不用在程序里面动态指明这就需要一种服务发现机制。Java SPI 就是提供了这样一个机制:**为某个接口寻找服务实现的机制。这有点类似 IoC 的思想,将装配的控制权移交到了程序之外。**
SPI 机制也解决了 Java 类加载体系中双亲委派模型带来的限制。[双亲委派模型](https://javaguide.cn/java/jvm/classloader.html)虽然保证了核心库的安全性和一致性但也限制了核心库或扩展库加载应用程序类路径上的类通常由第三方实现。SPI 允许核心或扩展库定义服务接口第三方开发者提供并部署实现SPI 服务加载机制则在运行时动态发现并加载这些实现。例如JDBC 4.0 及之后版本利用 SPI 自动发现和加载数据库驱动,开发者只需将驱动 JAR 包放置在类路径下即可,无需使用`Class.forName()`显式加载驱动类。
## SPI 介绍
@ -332,6 +332,10 @@ public void reload() {
}
```
其解决第三方类加载的机制其实就蕴含在 `ClassLoader cl = Thread.currentThread().getContextClassLoader();` 中,`cl` 就是**线程上下文类加载器**Thread Context ClassLoader。这是每个线程持有的类加载器JDK 的设计允许应用程序或容器(如 Web 应用服务器)设置这个类加载器,以便核心类库能够通过它来加载应用程序类。
线程上下文类加载器默认情况下是应用程序类加载器Application ClassLoader它负责加载 classpath 上的类。当核心库需要加载应用程序提供的类时,它可以使用线程上下文类加载器来完成。这样,即使是由引导类加载器加载的核心库代码,也能够加载并使用由应用程序类加载器加载的类。
根据代码的调用顺序,在 `reload()` 方法中是通过一个内部类 `LazyIterator` 实现的。先继续往下面看。
`ServiceLoader` 实现了 `Iterable` 接口的方法后,具有了迭代的能力,在这个 `iterator` 方法被调用时,首先会在 `ServiceLoader``Provider` 缓存中进行查找,如果缓存中没有命中那么则在 `LazyIterator` 中进行查找。

View File

@ -90,7 +90,7 @@ public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneabl
static final int MIN_TREEIFY_CAPACITY = 64;
// 存储元素的数组总是2的幂次倍
transient Node<k,v>[] table;
// 存放具体元素的集
// 一个包含了映射中所有键值对的集合视图
transient Set<map.entry<k,v>> entrySet;
// 存放元素的个数,注意这个不等于数组的长度。
transient int size;

View File

@ -18,7 +18,7 @@ I/O 一直是很多小伙伴难以理解的一个知识点,这篇文章我会
### 何为 I/O?
I/O**I**nput/**O**utpu 即**输入/输出** 。
I/O**I**nput/**O**utput 即**输入/输出** 。
**我们先从计算机结构的角度来解读一下 I/O。**