Skip to content

Commit

Permalink
feat: auto upload
Browse files Browse the repository at this point in the history
  • Loading branch information
WL2O2O committed Mar 12, 2024
1 parent 07b9b97 commit 8e28f1d
Showing 1 changed file with 105 additions and 0 deletions.
105 changes: 105 additions & 0 deletions src/bagu/Algo/e签宝24春招笔试.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,109 @@ public static void main(String[] args) throws InterruptedException, ExecutionExc
executor.shutdown();
}
```
> 等等,绝对不可能,今天的我发现了昨天很傻的我,我发现了问题所在!用`STAR`原则来分析一下:
- Situation(情景):懒汉单例模式不是线程安全的
- Task(任务):模拟实现(证明)线程不安全
- Action(行动):通过拆分分析代码,只有当多个线程同时发现当前实例为空时,才会`new`新的实例,因此增加线程冲突率,减少循环次数(多次循环会堵塞)
- Result(结果):去掉线程间睡眠时间、减少为两个循环,以此增加碰撞率,提高了创建多例可能性

```java
package singletondesignpattern;

/**
* @Author <a href="https://github.com/wl2o2o">程序员CSGUIDER</a>
* @From <a href="https://wl2o2o.github.io">CSGUIDER博客</a>
* @CreateTime 2024/3/12
*/

public class NonThreadSafeSingleton {
private static NonThreadSafeSingleton instance;

private NonThreadSafeSingleton() {}

// 注意:这里没有加锁,所以是线程不安全的
public static NonThreadSafeSingleton getInstance() {
if (instance == null) {
// 在多线程环境下,两个线程可能同时发现 instance 为 null,并各自创建一个实例
instance = new NonThreadSafeSingleton();
}
return instance;
}

@Override
public String toString() {
return "NonThreadSafeSingleton@" + Integer.toHexString(hashCode());
}

public static void main(String[] args) {
// 创建两个并发线程来获取单例
for (int i = 0; i < 2; i++) {
new Thread(() -> {
System.out.println("Thread " + Thread.currentThread().getName() + ": " + NonThreadSafeSingleton.getInstance());
}).start();
}
}
}
```
模拟成功截图:
![e签宝24春招笔试-2024-03-13-01-23-10](https://cdn.jsdelivr.net/gh/wl2o2o/blogCdn/img/e签宝24春招笔试-2024-03-13-01-23-10.png)

#### 模拟实现加同步锁,一定安全代码:

按照上述方法加上`synchronized`同步锁关键字,或者使用双重校验锁,也或者可以使用`ConcurrentHashMap`,都可以解决线程安全问题。

`ConcurrentHashMap`为例:

```java
public class ConcurrentSingleton {
private static ConcurrentHashMap<String, ConcurrentSingleton> map = new ConcurrentHashMap<>();
private static final String KEY = "key";
private ConcurrentSingleton() {}
public static ConcurrentSingleton getInstance() {
return map.computeIfAbsent(KEY, k -> {
ConcurrentSingleton instance = new ConcurrentSingleton();
return instance;
});
}
@Override
public String toString() {
return "ConcurrentSingleton@" + Integer.toHexString(hashCode());
}
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(() -> {
System.out.println("Thread " + Thread.currentThread().getName() + ": " + ConcurrentSingleton.getInstance());
}).start();
}
}
}
```

实现线程安全的单例模式还有很多种方式,比如:
- 使用饿汉式单例模式,
- 使用静态内部类,
- 使用双重校验锁,
- 使用枚举类,
- 使用`synchronized`关键字,
- 使用`ConcurrentHashMap`
- 使用`ThreadLocal`
- 使用`volatile`关键字,
- 使用`AtomicReference`
- 使用`Unsafe`
- 使用`JUC``AtomicReference`
- 使用`JUC``AtomicStampedReference`
- 使用`JUC``AtomicMarkableReference`
- 使用`JUC``AtomicReferenceArray`

#### 总结

总之,实现相对线程安全的单例模式方法有很多,但是,无论怎么实现,都必须保证只有一个实例,而且必须是线程安全的。

其实,为了保证线程安全,很多方法,都要写很多代码,代码极其臃肿。那么,有没有一种方法,可以保证线程安全,又可以保证代码精简呢?

其实,最简单的单例模式实现方式其实是枚举··· ···




0 comments on commit 8e28f1d

Please sign in to comment.