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 }

作者:别先生

博客园:IT虾米网

如果您想及时得到个人撰写文章以及著作的消息推送,可以扫描上方二维码,关注个人公众号哦。


发布评论
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

Java并发编程与高并发之线程安全性(原子性、可见性、有序性)知识解答
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。