Skip to content

Commit e322ee6

Browse files
committed
Update Java Notes
1 parent 2622550 commit e322ee6

File tree

2 files changed

+16
-180
lines changed

2 files changed

+16
-180
lines changed

DB.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10931,7 +10931,7 @@ Read-Through Pattern 也存在首次不命中的问题,采用缓存预热解
1093110931
1093210932
#### 缓存击穿
1093310933
10934-
场景:系统平稳运行过程中,数据库连接量瞬间激增,Redis服务器无大量key过期,Redis内存平稳,无波动,Redis服务器CPU正常,但是数据库崩溃
10934+
场景:系统平稳运行过程中,数据库连接量瞬间激增,Redis服务器无大量key过期,Redis内存平稳,无波动,Redis 服务器 CPU 正常,但是数据库崩溃
1093510935
1093610936
问题排查:
1093710937

Java.md

Lines changed: 15 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -7384,7 +7384,7 @@ ObjectInputStream ObjectOutputStream
73847384

73857385
#### 字节流
73867386

7387-
##### 字节输入流
7387+
##### 字节输入
73887388

73897389
FileInputStream文件字节输入流:
73907390

@@ -7460,7 +7460,7 @@ System.out.println(rs);
74607460

74617461

74627462

7463-
##### 字节输出流
7463+
##### 字节输出
74647464

74657465
FileOutputStream文件字节输出流:
74667466

@@ -7552,9 +7552,9 @@ public class CopyDemo01 {
75527552

75537553
#### 字符流
75547554

7555-
##### 字符输入流
7555+
##### 字符输入
75567556

7557-
FileReader:文件字符输入流
7557+
FileReader文件字符输入流
75587558

75597559
* 作用:以内存为基准,把磁盘文件的数据以字符的形式读入到内存,读取文本文件内容到内存中去。
75607560
* 构造器:
@@ -7606,9 +7606,9 @@ public class FileReaderDemo02 {//字符数组
76067606

76077607

76087608

7609-
##### 字符输出流
7609+
##### 字符输出
76107610

7611-
FileWriter文件字符输出流的使用。
7611+
FileWriter:文件字符输出流
76127612

76137613
* 作用:以内存为基准,把内存中的数据按照字符的形式写出到磁盘文件中去
76147614
* 构造器:
@@ -7941,6 +7941,10 @@ class User implements Serializable {
79417941

79427942

79437943

7944+
****
7945+
7946+
7947+
79447948
##### 反序列化
79457949

79467950
对象反序列化(对象字节输入流):ObjectInputStream
@@ -12469,23 +12473,23 @@ public void localvarGC4() {
1246912473

1247012474
安全点 (Safepoint):程序执行时并非在所有地方都能停顿下来开始GC,只有在安全点才能停下
1247112475

12472-
- Safe Point的选择很重要,如果太少可能导致GC等待的时间太长,如果太多可能导致运行时的性能问题
12473-
- 大部分指令的执行时间都非常短,通常会根据是否具有让程序长时间执行的特征为标准,选择些执行时间较长的指令作为Safe Point, 如方法调用、循环跳转和异常跳转等
12476+
- Safe Point 的选择很重要,如果太少可能导致GC等待的时间太长,如果太多可能导致运行时的性能问题
12477+
- 大部分指令的执行时间都非常短,通常会根据是否具有让程序长时间执行的特征为标准,选择些执行时间较长的指令作为 Safe Point, 如方法调用、循环跳转和异常跳转等
1247412478

1247512479
在GC发生时,让所有线程都在最近的安全点停顿下来的方法:
1247612480

1247712481
- 抢先式中断:没有虚拟机采用,首先中断所有线程,如果有线程不在安全点,就恢复线程让线程运行到安全点
1247812482
- 主动式中断:设置一个中断标志,各个线程运行到各个 Safe Point 时就轮询这个标志,如果中断标志为真,则将自己进行中断挂起
1247912483

12480-
问题:Safepoint 保证程序执行时,在不太长的时间内就会遇到可进入GC的Safepoint,但是当线程处于 Waiting 状态或Blocked状态,线程无法响应JVM的中断请求,运行到安全点去中断挂起,JVM也不可能等待线程被唤醒,对于这种情况,需要安全区域来解决
12484+
问题:Safepoint 保证程序执行时,在不太长的时间内就会遇到可进入GC的 Safepoint,但是当线程处于 Waiting 状态或 Blocked 状态,线程无法响应JVM的中断请求,运行到安全点去中断挂起,JVM也不可能等待线程被唤醒,对于这种情况,需要安全区域来解决
1248112485

1248212486
安全区域 (Safe Region):指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的
1248312487

1248412488
运行流程:
1248512489

12486-
- 当线程运行到Safe Region的代码时,首先标识已经进入了Safe Region,如果这段时间内发生GC,JVM会忽略标识为Safe Region状态的线程
12490+
- 当线程运行到 Safe Region 的代码时,首先标识已经进入了 Safe Region,如果这段时间内发生GC,JVM会忽略标识为 Safe Region 状态的线程
1248712491

12488-
- 当线程即将离开Safe Region时,会检查JVM是否已经完成GC,如果完成了则继续运行,否则线程必须等待GC完成,收到可以安全离开SafeRegion的信号
12492+
- 当线程即将离开 Safe Region 时,会检查JVM是否已经完成GC,如果完成了则继续运行,否则线程必须等待GC 完成,收到可以安全离开 SafeRegion 的信号
1248912493

1249012494

1249112495

@@ -18638,174 +18642,6 @@ public class TestVolatile {
1863818642

1863918643

1864018644

18641-
***
18642-
18643-
18644-
18645-
#### 单例模式
18646-
18647-
##### 饿汉式
18648-
18649-
单例模式有很多实现方法,饿汉、懒汉、静态内部类、枚举类,试分析每种实现下获取单例对象时的线程安全
18650-
18651-
* 饿汉式:类加载就会导致该单实例对象被创建
18652-
18653-
* 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
18654-
18655-
```java
18656-
public final class Singleton implements Serializable {
18657-
private Singleton() {}
18658-
private static final Singleton INSTANCE = new Singleton();
18659-
public static Singleton getInstance() {
18660-
return INSTANCE;
18661-
}
18662-
18663-
//解决序列化问题
18664-
protected Object readResolve() {
18665-
return INSTANCE;
18666-
}
18667-
}
18668-
```
18669-
18670-
* 问题1:为什么类加 final 修饰?
18671-
不被子类继承,防止子类中不适当的行为覆盖父类的方法,破坏了单例
18672-
18673-
* 问题2:如果实现了序列化接口,怎么防止防止反序列化破坏单例?
18674-
18675-
将单例对象序列化再反序列化,对象从内存反序列化到程序中会重新创建一个对象,通过反序列化得到的对象是不同的对象,而且得到的对象不是通过构造器得到的,反序列化得到的对象不执行构造器
18676-
18677-
解决办法:
18678-
18679-
* 对单例声明 transient,然后实现 readObject(ObjectInputStream in) 方法,复用原来的单例
18680-
18681-
条件:访问权限为private/protected、返回值必须是Object、异常可以不抛
18682-
* 实现readResolve()方法,当 JVM 从内存中反序列化地"组装"一个新对象,就会自动调用readResolve方法返回原来单例
18683-
18684-
* 问题3:为什么构造方法设置为私有? 是否能防止反射创建新的实例?
18685-
防止其他类无限创建对象;不能防止反射破坏
18686-
18687-
* 问题4:这种方式是否能保证单例对象创建时的线程安全?
18688-
能,静态变量初始化在类加载时完成,由JVM保证线程安全
18689-
18690-
* 问题5:为什么提供静态方法而不是直接将 INSTANCE 设置为 public?
18691-
更好的封装性、提供泛型支持、可以改进成懒汉单例设计
18692-
18693-
18694-
18695-
***
18696-
18697-
18698-
18699-
##### 枚举式
18700-
18701-
```java
18702-
enum Singleton {
18703-
INSTANCE;//相当于枚举类的静态成员变量
18704-
18705-
public void doSomething() {
18706-
System.out.println("doSomething");
18707-
}
18708-
}
18709-
public static void main(String[] args) {
18710-
Singleton.INSTANCE.doSomething();
18711-
}
18712-
```
18713-
18714-
* 问题1:枚举单例是如何限制实例个数的?每个枚举项都是一个实例,是一个静态成员变量
18715-
* 问题2:枚举单例在创建时是否有并发问题?否
18716-
* 问题3:枚举单例能否被反射破坏单例?否,反射创建对象时判断是枚举类型就直接抛出异常
18717-
* 问题4:枚举单例能否被反序列化破坏单例?否
18718-
* 问题5:枚举单例属于懒汉式还是饿汉式?**饿汉式**
18719-
* 问题6:枚举单例如果希望加入一些单例创建时的初始化逻辑该如何做?添加构造方法
18720-
18721-
反编译后的:
18722-
18723-
```java
18724-
public final class Singleton extends java.lang.Enum<Singleton> {//Enum实现序列化接口
18725-
public static final Singleton INSTANCE = new Singleton();
18726-
}
18727-
```
18728-
18729-
18730-
18731-
***
18732-
18733-
18734-
18735-
##### 懒汉式
18736-
18737-
```java
18738-
public final class Singleton {
18739-
private Singleton() { }
18740-
private static Singleton INSTANCE = null;
18741-
//分析这里的线程安全,并说明有什么缺点?锁范围太大,每次进入都要加锁
18742-
public static synchronized Singleton getInstance() {
18743-
if( INSTANCE != null ){
18744-
return INSTANCE;
18745-
}
18746-
INSTANCE = new Singleton();
18747-
return INSTANCE;
18748-
}
18749-
}
18750-
```
18751-
18752-
18753-
18754-
****
18755-
18756-
18757-
18758-
##### DCL
18759-
18760-
```java
18761-
public final class Singleton {
18762-
private Singleton() { }
18763-
// 防止指令重排序
18764-
private static volatile Singleton INSTANCE = null;
18765-
// 首次使用getInstance()才加锁,后续使用时无需加锁,性能好
18766-
public static Singleton getInstance() {
18767-
if (INSTANCE != null) {
18768-
return INSTANCE;
18769-
}
18770-
synchronized (Singleton.class) {
18771-
// 防止首次创建Singleton时的并发的问题
18772-
if (INSTANCE != null) { // t2
18773-
return INSTANCE;
18774-
}
18775-
INSTANCE = new Singleton();
18776-
return INSTANCE;
18777-
}
18778-
}
18779-
}
18780-
```
18781-
18782-
18783-
18784-
***
18785-
18786-
18787-
18788-
##### 内部类
18789-
18790-
```java
18791-
public final class Singleton {
18792-
private Singleton() { }
18793-
18794-
private static class LazyHolder {
18795-
static final Singleton INSTANCE = new Singleton();
18796-
}
18797-
18798-
public static Singleton getInstance() {
18799-
return LazyHolder.INSTANCE;
18800-
}
18801-
}
18802-
```
18803-
18804-
* 问题1:属于懒汉式还是饿汉式?
18805-
懒汉式,类加载本身就是懒惰的,首次调用时加载,然后对单例进行初始化
18806-
* 问题2:在创建时是否有线程安全问题?
18807-
没有,静态变量初始化在类加载时完成,由JVM保证线程安全
18808-
1880918645

1881018646

1881118647
***

0 commit comments

Comments
 (0)