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