1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

[docs update]部分描述完善

This commit is contained in:
Guide 2024-07-27 18:18:34 +08:00
parent 0574285032
commit 94a0c4ceb7
4 changed files with 91 additions and 49 deletions

View File

@ -202,13 +202,11 @@ Redlock 是直接操作 Redis 节点的,并不是通过 Redis 集群操作的
Redlock 实现比较复杂,性能比较差,发生时钟变迁的情况下还存在安全性隐患。《数据密集型应用系统设计》一书的作者 Martin Kleppmann 曾经专门发文([How to do distributed locking - Martin Kleppmann - 2016](https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html))怼过 Redlock他认为这是一个很差的分布式锁实现。感兴趣的朋友可以看看[Redis 锁从面试连环炮聊到神仙打架](https://mp.weixin.qq.com/s?__biz=Mzg3NjU3NTkwMQ==&mid=2247505097&idx=1&sn=5c03cb769c4458350f4d4a321ad51f5a&source=41#wechat_redirect)这篇文章,有详细介绍到 antirez 和 Martin Kleppmann 关于 Redlock 的激烈辩论。
实际项目中不建议使用 Redlock 算法,成本和收益不成正比。
如果不是非要实现绝对可靠的分布式锁的话,其实单机版 Redis 就完全够了,实现简单,性能也非常高。如果你必须要实现一个绝对可靠的分布式锁的话,可以基于 ZooKeeper 来做,只是性能会差一些。
实际项目中不建议使用 Redlock 算法,成本和收益不成正比,可以考虑基于 Redis 主从复制+哨兵模式实现分布式锁。
## 基于 ZooKeeper 实现分布式锁
Redis 实现分布式锁性能较高ZooKeeper 实现分布式锁可靠性更高。实际项目中,我们应该根据业务的具体需求来选择
ZooKeeper 相比于 Redis 实现分布式锁,除了提供相对更高的可靠性之外,在功能层面还有一个非常有用的特性:**Watch 机制**。这个机制可以用来实现公平的分布式锁。不过,使用 ZooKeeper 实现的分布式锁在性能方面相对较差因此如果对性能要求比较高的话ZooKeeper 可能就不太适合了
### 如何基于 ZooKeeper 实现分布式锁?
@ -365,14 +363,19 @@ private static class LockData
## 总结
在这篇文章中,我介绍了实现分布式锁的两种常见方式: Redis 和 ZooKeeper。至于具体选择 Redis 还是 ZooKeeper 来实现分布式锁,还是要看业务的具体需求
在这篇文章中,我介绍了实现分布式锁的两种常见方式:**Redis** 和 **ZooKeeper**。至于具体选择 Redis 还是 ZooKeeper 来实现分布式锁,还是要根据业务的具体需求来决定
- 如果对性能要求比较高的话,建议使用 Redis 实现分布式锁(优先选择 Redisson 提供的现成的分布式锁,而不是自己实现)
- 如果对可靠性要求比较高的话,建议使用 ZooKeeper 实现分布式锁(推荐基于 Curator 框架实现)。不过,现在很多项目都不会用到 ZooKeeper如果单纯是因为分布式锁而引入 ZooKeeper 的话,那是不太可取的,不建议这样做,为了一个小小的功能增加了系统的复杂度。
- 如果对性能要求比较高的话,建议使用 Redis 实现分布式锁。推荐优先选择 **Redisson** 提供的现成分布式锁,而不是自己实现。实际项目中不建议使用 Redlock 算法,成本和收益不成正比,可以考虑基于 Redis 主从复制+哨兵模式实现分布式锁
- 如果对可靠性要求比较高,建议使用 ZooKeeper 实现分布式锁,推荐基于 **Curator** 框架来实现。不过,现在很多项目都不会用到 ZooKeeper如果单纯是因为分布式锁而引入 ZooKeeper 的话,那是不太可取的,不建议这样做,为了一个小小的功能增加了系统的复杂度。
最后,再分享两篇我觉得写的还不错的文章:
需要注意的是,无论选择哪种方式实现分布式锁,包括 Redis、ZooKeeper 或 Etcd本文没介绍但也经常用来实现分布式锁都无法保证 100% 的安全性特别是在遇到进程垃圾回收GC、网络延迟等异常情况下。
为了进一步提高系统的可靠性,建议引入一个兜底机制。例如,可以通过 **版本号Fencing Token机制** 来避免并发冲突。
最后,再分享几篇我觉得写的还不错的文章:
- [分布式锁实现原理与最佳实践 - 阿里云开发者](https://mp.weixin.qq.com/s/JzCHpIOiFVmBoAko58ZuGw)
- [聊聊分布式锁 - 字节跳动技术团队](https://mp.weixin.qq.com/s/-N4x6EkxwAYDGdJhwvmZLw)
- [Redis、ZooKeeper、Etcd谁有最好用的分布式锁 - 腾讯云开发者](https://mp.weixin.qq.com/s/yZC6VJGxt1ANZkn0SljZBg)
<!-- @include: @article-footer.snippet.md -->

View File

@ -50,6 +50,8 @@ head:
Java 虚拟机Java Virtual Machine, JVM是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现WindowsLinuxmacOS目的是使用相同的字节码它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语言“一次编译,随处可以运行”的关键所在。
如下图所示不同编程语言Java、Groovy、Kotlin、JRuby、Clojure ...)通过各自的编译器编译成 `.class` 文件,并最终通过 JVM 在不同平台Windows、Mac、Linux上运行。
![运行在 Java 虚拟机之上的编程语言](https://oss.javaguide.cn/github/javaguide/java/basis/java-virtual-machine-program-language-os.png)
**JVM 并不是只有一种!只要满足 JVM 规范,每个公司、组织或者个人都可以开发自己的专属 JVM。** 也就是说我们平时接触到的 HotSpot VM 仅仅是是 JVM 规范的一种实现而已。
@ -60,11 +62,18 @@ Java 虚拟机Java Virtual Machine, JVM是运行 Java 字节码的虚拟
#### JDK 和 JRE
JDKJava Development Kit,它是功能齐全的 Java SDK是提供给开发者使用能够创建和编译 Java 程序的开发套件。它包含了 JRE同时还包含了编译 java 源码的编译器 javac 以及一些其他工具比如 javadoc文档注释工具、jdb调试器、jconsole基于 JMX 的可视化监控⼯具、javap反编译工具等。
JDKJava Development Kit是一个功能齐全的 Java 开发工具包,供开发者使用,用于创建和编译 Java 程序。它包含了 JREJava Runtime Environment以及编译器 javac 和其他工具,如 javadoc文档生成器、jdb调试器、jconsole监控工具、javap反编译工具等。
JREJava Runtime Environment 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,主要包括 Java 虚拟机JVM、Java 基础类库Class Library
JRE 是运行已编译 Java 程序所需的环境,主要包含以下两个部分:
也就是说JRE 是 Java 运行时环境,仅包含 Java 应用程序的运行时环境和必要的类库。而 JDK 则包含了 JRE同时还包括了 javac、javadoc、jdb、jconsole、javap 等工具,可以用于 Java 应用程序的开发和调试。如果需要进行 Java 编程工作,比如编写和编译 Java 程序、使用 Java API 文档等,就需要安装 JDK。而对于某些需要使用 Java 特性的应用程序,如 JSP 转换为 Java Servlet、使用反射等也需要 JDK 来编译和运行 Java 代码。因此,即使不打算进行 Java 应用程序的开发工作,也有可能需要安装 JDK。
1. **JVM** : 也就是我们上面提到的 Java 虚拟机。
2. **Java 基础类库Class Library**:一组标准的类库,提供常用的功能和 API如 I/O 操作、网络通信、数据结构等)。
简单来说JRE 只包含运行 Java 程序所需的环境和类库,而 JDK 不仅包含 JRE还包括用于开发和调试 Java 程序的工具。
如果需要编写、编译 Java 程序或使用 Java API 文档,就需要安装 JDK。某些需要 Java 特性的应用程序(如 JSP 转换为 Servlet 或使用反射)也可能需要 JDK 来编译和运行 Java 代码。因此,即使不进行 Java 开发工作,有时也可能需要安装 JDK。
下图清晰展示了 JDK、JRE 和 JVM 的关系。
![jdk-include-jre](https://oss.javaguide.cn/github/javaguide/java/basis/jdk-include-jre.png)
@ -272,9 +281,26 @@ Java 中的注释有三种:
### 自增自减运算符
在写代码的过程中,常见的一种情况是需要某个整数类型变量增加 1 或减少 1Java 提供了一种特殊的运算符,用于这种表达式,叫做自增运算符(++)和自减运算符(--
在写代码的过程中,常见的一种情况是需要某个整数类型变量增加 1 或减少 1。Java 提供了自增运算符 (`++`) 和自减运算符 (`--`) 来简化这种操作
++ 和 -- 运算符可以放在变量之前,也可以放在变量之后,当运算符放在变量之前时(前缀),先自增/减,再赋值;当运算符放在变量之后时(后缀),先赋值,再自增/减。例如,当 `b = ++a` 时,先自增(自己增加 1再赋值赋值给 b`b = a++` 时,先赋值(赋值给 b),再自增(自己增加 1。也就是++a 输出的是 a+1 的值a++输出的是 a 值。用一句口诀就是:“符号在前就先加/减,符号在后就后加/减”。
`++``--` 运算符可以放在变量之前,也可以放在变量之后:
- **前缀形式**(例如 `++a``--a`):先自增/自减变量的值,然后再使用该变量,例如,`b = ++a` 先将 `a` 增加 1然后把增加后的值赋给 `b`
- **后缀形式**(例如 `a++``a--`):先使用变量的当前值,然后再自增/自减变量的值。例如,`b = a++` 先将 `a` 的当前值赋给 `b`,然后再将 `a` 增加 1。
为了方便记忆,可以使用下面的口诀:**符号在前就先加/减,符号在后就后加/减**。
下面来看一个考察自增自减运算符的高频笔试题:执行下面的代码后,`a``b``c``d``e`的值是?
```java
int a = 9;
int b = a++;
int c = ++a;
int d = c--;
int e = --d;
```
答案:`a = 11``b = 9``c = 10``d = 10``e = 10`
### 移位运算符
@ -293,7 +319,18 @@ static final int hash(Object key) {
```
在 Java 代码里使用 `<<``>>``>>>`转换成的指令码运行起来会更高效些。
**使用移位运算符的主要原因**
1. **高效**:移位运算符直接对应于处理器的移位指令。现代处理器具有专门的硬件指令来执行这些移位操作,这些指令通常在一个时钟周期内完成。相比之下,乘法和除法等算术运算在硬件层面上需要更多的时钟周期来完成。
2. **节省内存**:通过移位操作,可以使用一个整数(如 `int``long`)来存储多个布尔值或标志位,从而节省内存。
移位运算符最常用于快速乘以或除以 2 的幂次方。除此之外,它还在以下方面发挥着重要作用:
- **位字段管理**:例如存储和操作多个布尔值。
- **哈希算法和加密解密**:通过移位和与、或等操作来混淆数据。
- **数据压缩**:例如霍夫曼编码通过移位运算符可以快速处理和操作二进制数据,以生成紧凑的压缩格式。
- **数据校验**:例如 CRC循环冗余校验通过移位和多项式除法生成和校验数据完整性。。
- **内存对齐**:通过移位操作,可以轻松计算和调整数据的对齐地址。
掌握最基本的移位运算符知识还是很有必要的,这不光可以帮助我们在代码中使用,还可以帮助我们理解源码中涉及到移位运算符的代码。
@ -303,6 +340,8 @@ Java 中有三种移位运算符:
- `>>` :带符号右移,向右移若干位,高位补符号位,低位丢弃。正数高位补 0,负数高位补 1。`x >> n`,相当于 x 除以 2 的 n 次方。
- `>>>` :无符号右移,忽略符号位,空位都以 0 补齐。
虽然移位运算本质上可以分为左移和右移,但在实际应用中,右移操作需要考虑符号位的处理方式。
由于 `double``float` 在二进制中的表现比较特殊,因此不能来进行移位操作。
移位操作符实际上支持的类型只有`int``long`,编译器在对`short``byte``char`类型进行移位前,都会将其转换为`int`类型再操作。

View File

@ -286,7 +286,7 @@ class GeneratorImpl<T> implements Generator<T>{
实现泛型接口,指定类型:
```java
class GeneratorImpl<T> implements Generator<String>{
class GeneratorImpl implements Generator<String> {
@Override
public String method() {
return "hello";