1、安全发布对象的发布与逃逸。
发布对象,使一个对象能够被当前范围之外的代码所使用。
对象逸出,一种错误的发布,当一个对象还没有构造完成时,就使它被其他线程所见。
如果不正确的发布了可变对象,会造成两种错误,首先是发布线程以外的任何线程都可以看到被发布对象的过期的值。其次呢,线程看到的被发布对象的引用是最新的,然而呢,被发布对象的状态却是过期的,如果一个对象是可变对象,那么它就要被安全发布才可以。
2、安全发布对象的四种方式。
1)、第一种,在静态初始化函数中初始化一个对象引用。 2)、第二种,将对象的引用保存到volatile类型域或者AtomicReference对象中。 3)、第三种,将对象的引用保存到某个正确构造对象的final类型域中。 4)、第四种,将对象的引用保存到一个由锁保护的域中。
2.1、懒汉模式 ,单例实例在第一次使用时进行创建,单线程运行没有问题的,但是多线程可能在getInstance出现线程不安全的情况。
1 package com.bie.concurrency.example.singleton;
2
3 import com.bie.concurrency.annoations.NotThreadSafe;
4
5 /**
6 *
7 *
8 * @Title: SingletonExample1.java
9 * @Package com.bie.concurrency.example.singleton
10 * @Description: TODO
11 * @author biehl
12 * @date 2020年1月6日
13 * @version V1.0
14 *
15 * 1、懒汉模式 ,单例实例在第一次使用时进行创建
16 *
17 * 2、单线程运行没有问题的,但是多线程可能在getInstance出现线程不安全的情况
18 *
19 * 3、第一种,在静态初始化函数中初始化一个对象引用。
20 *
21 */
22 @NotThreadSafe
23 public class SingletonExample1 {
24
25 // 私有的默认构造方法,避免外部通过new创建对象。
26 private SingletonExample1() {
27 }
28
29 // 定义单例对象,至少保证有一个对象被创建的。
30 private static SingletonExample1 singletonExample1 = null;
31
32 // 静态工厂方法
33 public static SingletonExample1 getInstance() {
34 // 判断对象是否为null和创建对象,此处导致了线程不安全
35 if (null == singletonExample1) {
36 singletonExample1 = new SingletonExample1();
37 }
38 return singletonExample1;
39 }
40
41 }
2.2、饿汉模式 ,单例实例在类装载时进行创建,饿汉模式是线程安全的。饿汉模式,如果单例类构造方法中没有过多的操作处理,饿汉模式是可以接受的。饿汉模式的缺点,如果单例类构造方法中存在过多的操作处理,会导致该类加载的过慢。可能会引起性能问题。
1 package com.bie.concurrency.example.singleton;
2
3 import javax.annotation.concurrent.ThreadSafe;
4
5 /**
6 *
7 *
8 * @Title: SingletonExample1.java
9 * @Package com.bie.concurrency.example.singleton
10 * @Description: TODO
11 * @author biehl
12 * @date 2020年1月6日
13 * @version V1.0
14 *
15 * 1、饿汉模式 ,单例实例在类装载时进行创建,饿汉模式是线程安全的。
16 *
17 * 2、饿汉模式,如果单例类构造方法中没有过多的操作处理,饿汉模式是可以接受的。
18 *
19 * 3、饿汉模式的缺点,如果单例类构造方法中存在过多的操作处理,会导致该类加载的过慢。可能会引起性能问题。
20 *
21 * 4、第一种,在静态初始化函数中初始化一个对象引用。
22 *
23 */
24 @ThreadSafe
25 public class SingletonExample2 {
26
27 // 私有的默认构造方法,避免外部通过new创建对象。
28 // 饿汉模式,私有构造方法没有过多处理。饿汉模式创建的对象肯定会在实际中被使用,不会造成资源浪费。
29 private SingletonExample2() {
30 }
31
32 // 定义单例对象,至少保证有一个对象被创建的。在类装载的时候进行创建保证了线程的安全性。
33 private static SingletonExample2 singletonExample2 = new SingletonExample2();
34
35 // 静态工厂方法
36 public static SingletonExample2 getInstance() {
37 return singletonExample2;
38 }
39
40 }
2.3、懒汉模式 ,单例实例在第一次使用时进行创建,方法加synchronized修饰,不推荐,虽然保证了线程安全性,但是带来了性能方面的开销。
1 package com.bie.concurrency.example.singleton;
2
3 import com.bie.concurrency.annoations.NotRecommend;
4 import com.bie.concurrency.annoations.ThreadSafe;
5
6 /**
7 *
8 *
9 * @Title: SingletonExample1.java
10 * @Package com.bie.concurrency.example.singleton
11 * @Description: TODO
12 * @author biehl
13 * @date 2020年1月6日
14 * @version V1.0
15 *
16 * 1、懒汉模式 ,单例实例在第一次使用时进行创建
17 *
18 * 2、方法加synchronized修饰,不推荐,虽然保证了线程安全性,但是带来了性能方面的开销。
19 *
20 * 3、第四种,将对象的引用保存到一个由锁保护的域中。
21 */
22 @ThreadSafe
23 @NotRecommend
24 public class SingletonExample3 {
25
26 // 私有的默认构造方法,避免外部通过new创建对象。
27 private SingletonExample3() {
28 }
29
30 // 定义单例对象,至少保证有一个对象被创建的。
31 private static SingletonExample3 singletonExample3 = null;
32
33 // 静态工厂方法
34 // 使用synchronized修饰,方法内部所有实现同一时间内只能由一个线程访问。
35 // 因此可以保证线程安全的。
36 public static synchronized SingletonExample3 getInstance() {
37 if (null == singletonExample3) {
38 singletonExample3 = new SingletonExample3();
39 }
40 return singletonExample3;
41 }
42
43 }
2.4、懒汉模式,【双重同步锁单例模式 】,单例实例在第一次使用时进行创建,此实现是,线程不安全的,JVM和cpu优化,发生了指令重排。
1 package com.bie.concurrency.example.singleton;
2
3 import com.bie.concurrency.annoations.NotThreadSafe;
4
5 /**
6 *
7 *
8 * @Title: SingletonExample1.java
9 * @Package com.bie.concurrency.example.singleton
10 * @Description: TODO
11 * @author biehl
12 * @date 2020年1月6日
13 * @version V1.0
14 *
15 * 1、懒汉模式,【双重同步锁单例模式 】,单例实例在第一次使用时进行创建
16 *
17 * 2、此实现是,线程不安全的,JVM和cpu优化,发生了指令重排
18 *
19 */
20 @NotThreadSafe
21 public class SingletonExample4 {
22
23 // 1、memory = allocate() 分配对象的内存空间
24 // 2、ctorInstance() 初始化对象
25 // 3、instance = memory 设置instance指向刚分配的内存
26
27 // JVM和cpu优化,发生了指令重排
28
29 // 1、memory = allocate() 分配对象的内存空间
30 // 3、instance = memory 设置instance指向刚分配的内存
31 // 2、ctorInstance() 初始化对象
32
33 // 私有的默认构造方法,避免外部通过new创建对象。
34 private SingletonExample4() {
35 }
36
37 // 定义单例对象,至少保证有一个对象被创建的。
38 private static SingletonExample4 singletonExample4 = null;
39
40 // 静态工厂方法
41 public static SingletonExample4 getInstance() {
42 // 双重检测机制
43 if (singletonExample4 == null) {
44 // 同步锁,判断对象不为空以后,锁着SingletonExample4类
45 // synchronized修饰的内部,同一时间只能由一个线程可以访问的。
46 synchronized (SingletonExample4.class) {
47 // 再次进行判断,如果singletonExample4为空,就进行创建对象。
48 if (singletonExample4 == null) {
49 singletonExample4 = new SingletonExample4();
50 }
51 }
52 }
53 return singletonExample4;
54 }
55
56 }
2.5、懒汉模式,【双重同步锁单例模式 】,单例实例在第一次使用时进行创建,此实现是,线程安全的,volatile禁止指令重排序。
1 package com.bie.concurrency.example.singleton;
2
3 import com.bie.concurrency.annoations.ThreadSafe;
4
5 /**
6 *
7 *
8 * @Title: SingletonExample1.java
9 * @Package com.bie.concurrency.example.singleton
10 * @Description: TODO
11 * @author biehl
12 * @date 2020年1月6日
13 * @version V1.0
14 *
15 * 1、懒汉模式,【双重同步锁单例模式 】,单例实例在第一次使用时进行创建
16 *
17 * 2、此实现是,线程安全的,volatile禁止指令重排序。
18 *
19 * 3、第二种,将对象的引用保存到volatile类型域或者AtomicReference对象中。
20 *
21 */
22 @ThreadSafe
23 public class SingletonExample5 {
24
25 // 1、memory = allocate() 分配对象的内存空间
26 // 2、ctorInstance() 初始化对象
27 // 3、instance = memory 设置instance指向刚分配的内存
28
29 // 私有的默认构造方法,避免外部通过new创建对象。
30 private SingletonExample5() {
31 }
32
33 // 定义单例对象,至少保证有一个对象被创建的。
34 // 单例对象 volatile + 双重检测机制 -> 禁止指令重排
35 // volatile适用场景做状态标识量、双重检测,此处就是volatile的双重检测使用场景。
36 private volatile static SingletonExample5 singletonExample4 = null;
37
38 // 静态工厂方法
39 public static SingletonExample5 getInstance() {
40 // 双重检测机制
41 if (singletonExample4 == null) {
42 // 同步锁,判断对象不为空以后,锁着SingletonExample4类
43 // synchronized修饰的内部,同一时间只能由一个线程可以访问的。
44 synchronized (SingletonExample5.class) {
45 // 再次进行判断,如果singletonExample4为空,就进行创建对象。
46 if (singletonExample4 == null) {
47 singletonExample4 = new SingletonExample5();
48 }
49 }
50 }
51 return singletonExample4;
52 }
53
54 }
2.6、饿汉模式 ,单例实例在类装载时进行创建,饿汉模式是线程安全的。
1 package com.bie.concurrency.example.singleton;
2
3 import javax.annotation.concurrent.ThreadSafe;
4
5 /**
6 *
7 *
8 * @Title: SingletonExample1.java
9 * @Package com.bie.concurrency.example.singleton
10 * @Description: TODO
11 * @author biehl
12 * @date 2020年1月6日
13 * @version V1.0
14 *
15 * 1、饿汉模式 ,单例实例在类装载时进行创建,饿汉模式是线程安全的。
16 *
17 *
18 */
19 @ThreadSafe
20 public class SingletonExample6 {
21
22 // 私有的默认构造方法,避免外部通过new创建对象。
23 // 饿汉模式,私有构造方法没有过多处理。饿汉模式创建的对象肯定会在实际中被使用,不会造成资源浪费。
24 private SingletonExample6() {
25 }
26
27 // 定义单例对象,至少保证有一个对象被创建的。在类装载的时候进行创建保证了线程的安全性。
28 private static SingletonExample6 singletonExample6 = null;
29
30 // 静态块初始化对象singletonExample6
31 static {
32 singletonExample6 = new SingletonExample6();
33 }
34
35 // 静态工厂方法
36 public static SingletonExample6 getInstance() {
37 return singletonExample6;
38 }
39
40 public static void main(String[] args) {
41 System.out.println(getInstance().hashCode());
42 System.out.println(getInstance().hashCode());
43 }
44
45 }
2.7、枚举方式。线程安全,推荐的方式。相比于懒汉模式,在安全性方面更容易保证,在饿汉模式,在安全性方面,在实际调用方面才可以初始化,不会造成资源的浪费。
1 package com.bie.concurrency.example.singleton;
2
3 import javax.annotation.concurrent.ThreadSafe;
4
5 import com.bie.concurrency.annoations.Recommend;
6
7 /**
8 *
9 *
10 * @Title: SingletonExample1.java
11 * @Package com.bie.concurrency.example.singleton
12 * @Description: TODO
13 * @author biehl
14 * @date 2020年1月6日
15 * @version V1.0
16 *
17 * 1、枚举方式。线程安全,推荐的方式。
18 *
19 * 2、相比于懒汉模式,在安全性方面更容易保证,在饿汉模式,在安全性方面,在实际调用方面才可以初始化,不会造成资源的浪费。
20 *
21 */
22 @ThreadSafe
23 @Recommend
24 public class SingletonExample7 {
25
26 // 私有的默认构造方法,避免外部通过new创建对象。
27 private SingletonExample7() {
28 }
29
30 // 静态工厂方法
31 public static SingletonExample7 getInstance() {
32 return Singleton.INSTANCE.getInstance();
33 }
34
35 // 枚举类,私有的枚举类。
36 private enum Singleton {
37 // instance
38 INSTANCE;
39
40 // 私有的类的实例
41 private SingletonExample7 singletonExample7;
42
43 // JVM保证这个方法绝对只调用一次
44 // 枚举类的构造方法
45 Singleton() {
46 singletonExample7 = new SingletonExample7();
47 }
48
49 // 提供一个方法方便类来获取
50 public SingletonExample7 getInstance() {
51 // 返回枚举类里面的实例
52 return singletonExample7;
53 }
54 }
55
56 }