mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-06-16 18:10:13 +08:00
[docs update]typo
This commit is contained in:
parent
cafb410791
commit
78fee25ee8
@ -38,7 +38,7 @@ icon: "lock"
|
|||||||
|
|
||||||
### 如何基于 Redis 实现一个最简易的分布式锁?
|
### 如何基于 Redis 实现一个最简易的分布式锁?
|
||||||
|
|
||||||
不论是实现锁还是分布式锁,核心都在于“互斥”。
|
不论是本地锁还是分布式锁,核心都在于“互斥”。
|
||||||
|
|
||||||
在 Redis 中, `SETNX` 命令是可以帮助我们实现互斥。`SETNX` 即 **SET** if **N**ot e**X**ists (对应 Java 中的 `setIfAbsent` 方法),如果 key 不存在的话,才会设置 key 的值。如果 key 已经存在, `SETNX` 啥也不做。
|
在 Redis 中, `SETNX` 命令是可以帮助我们实现互斥。`SETNX` 即 **SET** if **N**ot e**X**ists (对应 Java 中的 `setIfAbsent` 方法),如果 key 不存在的话,才会设置 key 的值。如果 key 已经存在, `SETNX` 啥也不做。
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ icon: "lock"
|
|||||||
(integer) 1
|
(integer) 1
|
||||||
```
|
```
|
||||||
|
|
||||||
为了误删到其他的锁,这里我们建议使用 Lua 脚本通过 key 对应的 value(唯一值)来判断。
|
为了防止误删到其他的锁,这里我们建议使用 Lua 脚本通过 key 对应的 value(唯一值)来判断。
|
||||||
|
|
||||||
选用 Lua 脚本是为了保证解锁操作的原子性。因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,从而保证了锁释放操作的原子性。
|
选用 Lua 脚本是为了保证解锁操作的原子性。因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,从而保证了锁释放操作的原子性。
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class Class<T> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
简单来说,**类加载器的主要作用就是加载 Java 类的字节码( `.class` 文件)到 JVM 中(在内存中生成一个代表该类的 `Class` 对象)。** 字节码可以是 Java 源程序(`.java`文件)经过 javac 编译得来,也可以是通过工具动态生成或者通过网络下载得来。
|
简单来说,**类加载器的主要作用就是加载 Java 类的字节码( `.class` 文件)到 JVM 中(在内存中生成一个代表该类的 `Class` 对象)。** 字节码可以是 Java 源程序(`.java`文件)经过 `javac` 编译得来,也可以是通过工具动态生成或者通过网络下载得来。
|
||||||
|
|
||||||
其实除了加载类之外,类加载器还可以加载 Java 应用所需的资源如文本、图像、配置文件、视频等等文件资源。本文只讨论其核心功能:加载类。
|
其实除了加载类之外,类加载器还可以加载 Java 应用所需的资源如文本、图像、配置文件、视频等等文件资源。本文只讨论其核心功能:加载类。
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ JVM 中内置了三个重要的 `ClassLoader`:
|
|||||||
|
|
||||||
> 🌈 拓展一下:
|
> 🌈 拓展一下:
|
||||||
>
|
>
|
||||||
> - **`rt.jar`** : rt 代表“RunTime”,``rt.jar`是Java基础类库,包含Java doc里面看到的所有的类的类文件。也就是说,我们常用内置库 java.xxx.* 都在里面,比如`java.util._`、`java.io._`、`java.nio._`、`java.lang._`、`java.sql._`、`java.math._`。
|
> - **`rt.jar`** : rt 代表“RunTime”,`rt.jar`是Java基础类库,包含Java doc里面看到的所有的类的类文件。也就是说,我们常用内置库 `java.xxx.* `都在里面,比如`java.util.*`、`java.io.*`、`java.nio.*`、`java.lang.*`、`java.sql.*`、`java.math.*`。
|
||||||
> - Java 9 引入了模块系统,并且略微更改了上述的类加载器。扩展类加载器被改名为平台类加载器(platform class loader)。Java SE 中除了少数几个关键模块,比如说 `java.base` 是由启动类加载器加载之外,其他的模块均由平台类加载器所加载。
|
> - Java 9 引入了模块系统,并且略微更改了上述的类加载器。扩展类加载器被改名为平台类加载器(platform class loader)。Java SE 中除了少数几个关键模块,比如说 `java.base` 是由启动类加载器加载之外,其他的模块均由平台类加载器所加载。
|
||||||
|
|
||||||
除了这三种类加载器之外,用户还可以加入自定义的类加载器来进行拓展,以满足自己的特殊需求。就比如说,我们可以对 Java 类的字节码( `.class` 文件)进行加密,加载时再利用自定义的类加载器对其解密。
|
除了这三种类加载器之外,用户还可以加入自定义的类加载器来进行拓展,以满足自己的特殊需求。就比如说,我们可以对 Java 类的字节码( `.class` 文件)进行加密,加载时再利用自定义的类加载器对其解密。
|
||||||
@ -178,6 +178,8 @@ public class PrintClassLoaderTree {
|
|||||||
|
|
||||||
### 双亲委派模型介绍
|
### 双亲委派模型介绍
|
||||||
|
|
||||||
|
类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载呢?这就需要提到双亲委派模型了。
|
||||||
|
|
||||||
根据官网介绍:
|
根据官网介绍:
|
||||||
|
|
||||||
> The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.
|
> The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.
|
||||||
@ -197,6 +199,8 @@ public class PrintClassLoaderTree {
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
注意⚠️:双亲委派模型并不是一种强制性的约束,只是 JDK 官方推荐的一种方式。如果我们因为某些特殊需求想要打破双亲委派模型,也是可以的,后文会介绍具体的方法。
|
||||||
|
|
||||||
其实这个双亲翻译的容易让别人误解,我们一般理解的双亲都是父母,这里的双亲更多地表达的是“父母这一辈”的人而已,并不是说真的有一个 `MotherClassLoader` 和一个`FatherClassLoader` 。个人觉得翻译成单亲委派模型更好一些,不过,国内既然翻译成了双亲委派模型并流传了,按照这个来也没问题,不要被误解了就好。
|
其实这个双亲翻译的容易让别人误解,我们一般理解的双亲都是父母,这里的双亲更多地表达的是“父母这一辈”的人而已,并不是说真的有一个 `MotherClassLoader` 和一个`FatherClassLoader` 。个人觉得翻译成单亲委派模型更好一些,不过,国内既然翻译成了双亲委派模型并流传了,按照这个来也没问题,不要被误解了就好。
|
||||||
|
|
||||||
另外,类加载器之间的父子关系一般不是以继承的关系来实现的,而是通常使用组合关系来复用父加载器的代码。
|
另外,类加载器之间的父子关系一般不是以继承的关系来实现的,而是通常使用组合关系来复用父加载器的代码。
|
||||||
@ -286,6 +290,10 @@ protected Class<?> loadClass(String name, boolean resolve)
|
|||||||
|
|
||||||
**🐛 修正(参见:[issue871](https://github.com/Snailclimb/JavaGuide/issues/871) )** :自定义加载器的话,需要继承 `ClassLoader` 。如果我们不想打破双亲委派模型,就重写 `ClassLoader` 类中的 `findClass()` 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。但是,如果想打破双亲委派模型则需要重写 `loadClass()` 方法。
|
**🐛 修正(参见:[issue871](https://github.com/Snailclimb/JavaGuide/issues/871) )** :自定义加载器的话,需要继承 `ClassLoader` 。如果我们不想打破双亲委派模型,就重写 `ClassLoader` 类中的 `findClass()` 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。但是,如果想打破双亲委派模型则需要重写 `loadClass()` 方法。
|
||||||
|
|
||||||
|
为什么是重写 `loadClass()` 方法打破双亲委派模型呢?双亲委派模型的执行流程已经解释了:
|
||||||
|
|
||||||
|
> 类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父加载器 `loadClass()`方法来加载类)。
|
||||||
|
|
||||||
我们比较熟悉的 Tomcat 服务器为了能够优先加载 Web 应用目录下的类,然后再加载其他目录下的类,就自定义了类加载器 `WebAppClassLoader` 来打破双亲委托机制。这也是 Tomcat 下 Web 应用之间的类实现隔离的具体原理。
|
我们比较熟悉的 Tomcat 服务器为了能够优先加载 Web 应用目录下的类,然后再加载其他目录下的类,就自定义了类加载器 `WebAppClassLoader` 来打破双亲委托机制。这也是 Tomcat 下 Web 应用之间的类实现隔离的具体原理。
|
||||||
|
|
||||||
Tomcat 的类加载器的层次结构如下:
|
Tomcat 的类加载器的层次结构如下:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user