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虾米网
如果您想及时得到个人撰写文章以及著作的消息推送,可以扫描上方二维码,关注个人公众号哦。