Skip to content

Commit 8e28f1d

Browse files
committed
feat: auto upload
1 parent 07b9b97 commit 8e28f1d

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

src/bagu/Algo/e签宝24春招笔试.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,109 @@ public static void main(String[] args) throws InterruptedException, ExecutionExc
289289
executor.shutdown();
290290
}
291291
```
292+
> 等等,绝对不可能,今天的我发现了昨天很傻的我,我发现了问题所在!用`STAR`原则来分析一下:
293+
- Situation(情景):懒汉单例模式不是线程安全的
294+
- Task(任务):模拟实现(证明)线程不安全
295+
- Action(行动):通过拆分分析代码,只有当多个线程同时发现当前实例为空时,才会`new`新的实例,因此增加线程冲突率,减少循环次数(多次循环会堵塞)
296+
- Result(结果):去掉线程间睡眠时间、减少为两个循环,以此增加碰撞率,提高了创建多例可能性
292297

298+
```java
299+
package singletondesignpattern;
300+
301+
/**
302+
* @Author <a href="https://github.com/wl2o2o">程序员CSGUIDER</a>
303+
* @From <a href="https://wl2o2o.github.io">CSGUIDER博客</a>
304+
* @CreateTime 2024/3/12
305+
*/
306+
307+
public class NonThreadSafeSingleton {
308+
private static NonThreadSafeSingleton instance;
309+
310+
private NonThreadSafeSingleton() {}
311+
312+
// 注意:这里没有加锁,所以是线程不安全的
313+
public static NonThreadSafeSingleton getInstance() {
314+
if (instance == null) {
315+
// 在多线程环境下,两个线程可能同时发现 instance 为 null,并各自创建一个实例
316+
instance = new NonThreadSafeSingleton();
317+
}
318+
return instance;
319+
}
320+
321+
@Override
322+
public String toString() {
323+
return "NonThreadSafeSingleton@" + Integer.toHexString(hashCode());
324+
}
325+
326+
public static void main(String[] args) {
327+
// 创建两个并发线程来获取单例
328+
for (int i = 0; i < 2; i++) {
329+
new Thread(() -> {
330+
System.out.println("Thread " + Thread.currentThread().getName() + ": " + NonThreadSafeSingleton.getInstance());
331+
}).start();
332+
}
333+
}
334+
}
335+
```
336+
模拟成功截图:
337+
![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)
338+
339+
#### 模拟实现加同步锁,一定安全代码:
340+
341+
按照上述方法加上`synchronized`同步锁关键字,或者使用双重校验锁,也或者可以使用`ConcurrentHashMap`,都可以解决线程安全问题。
342+
343+
`ConcurrentHashMap`为例:
344+
345+
```java
346+
public class ConcurrentSingleton {
347+
private static ConcurrentHashMap<String, ConcurrentSingleton> map = new ConcurrentHashMap<>();
348+
private static final String KEY = "key";
349+
private ConcurrentSingleton() {}
350+
public static ConcurrentSingleton getInstance() {
351+
return map.computeIfAbsent(KEY, k -> {
352+
ConcurrentSingleton instance = new ConcurrentSingleton();
353+
return instance;
354+
});
355+
}
356+
@Override
357+
public String toString() {
358+
return "ConcurrentSingleton@" + Integer.toHexString(hashCode());
359+
}
360+
public static void main(String[] args) {
361+
for (int i = 0; i < 2; i++) {
362+
new Thread(() -> {
363+
System.out.println("Thread " + Thread.currentThread().getName() + ": " + ConcurrentSingleton.getInstance());
364+
}).start();
365+
}
366+
}
367+
}
368+
```
369+
370+
实现线程安全的单例模式还有很多种方式,比如:
371+
- 使用饿汉式单例模式,
372+
- 使用静态内部类,
373+
- 使用双重校验锁,
374+
- 使用枚举类,
375+
- 使用`synchronized`关键字,
376+
- 使用`ConcurrentHashMap`
377+
- 使用`ThreadLocal`
378+
- 使用`volatile`关键字,
379+
- 使用`AtomicReference`
380+
- 使用`Unsafe`
381+
- 使用`JUC``AtomicReference`
382+
- 使用`JUC``AtomicStampedReference`
383+
- 使用`JUC``AtomicMarkableReference`
384+
- 使用`JUC``AtomicReferenceArray`
385+
386+
#### 总结
387+
388+
总之,实现相对线程安全的单例模式方法有很多,但是,无论怎么实现,都必须保证只有一个实例,而且必须是线程安全的。
389+
390+
其实,为了保证线程安全,很多方法,都要写很多代码,代码极其臃肿。那么,有没有一种方法,可以保证线程安全,又可以保证代码精简呢?
391+
392+
其实,最简单的单例模式实现方式其实是枚举··· ···
393+
394+
395+
396+
397+

0 commit comments

Comments
 (0)