单例模式(Singleton):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
类图如下:
单例特点:
构造方法必须为private
,在类的内部自行实例化;public方法入口
,作为唯一调用单例类的途径得到实例。package com.wzj.singleton;
/**
* @Author: wzj
* @Date: 2020/2/13 12:41
* @Desc: 饿汉式
* 类加载到内存后,就实例化一个单例,JVM保证线程安全
* 唯一缺点:不管用到与否,类装载时就完成实例化
*/
public class HungrySingleton {
private static final HungrySingleton HUNGRY_SINGLETON = new HungrySingleton();
/**
* 私有构造方法,只有本类才能调用
*/
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return HUNGRY_SINGLETON;
}
public static void main(String[] args) {
HungrySingleton h1 = HungrySingleton.getInstance();
HungrySingleton h2 = HungrySingleton.getInstance();
System.out.println(h1 == h2);
}
}
执行结果:
true
final
的静态变量保证唯一实例。package com.wzj.singleton;
/**
* @Author: wzj
* @Date: 2020/2/13 12:50
* @Desc: 懒汉式-线程不安全
*/
public class LazySingleton {
private static LazySingleton LAZY_SINGLETON;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (null == LAZY_SINGLETON) {
//睡眠1毫秒,在new对象前,增加被其他线程打断的机会,保证能被多个线程执行
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
LAZY_SINGLETON = new LazySingleton();
}
return LAZY_SINGLETON;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() ->
System.out.println(LAZY_SINGLETON.getInstance().hashCode())
).start();
}
}
}
执行结果
1338303845
1276043710
874014640
2116194883
835777993
527405373
38860249
772510047
484927678
1375039115
.
.
.
getInstace
方法的时候创建实例,通过100个线程测试发现其hashcode
并不相等,并不是单例。package com.wzj.singleton;
/**
* @Author: wzj
* @Date: 2020/2/13 13:50
* @Desc: 懒汉式-线程安全
* 通过synchronized解决,但获取锁带来性能开销,效率下降
*/
public class ThreadSafeSingleton {
private static ThreadSafeSingleton LAZY_SINGLETON;
private ThreadSafeSingleton() {
}
public static synchronized ThreadSafeSingleton getInstance() {
if (null == LAZY_SINGLETON) {
//睡眠1毫秒,在new对象前,增加被其他线程打断的机会,保证能被多个线程执行
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
LAZY_SINGLETON = new ThreadSafeSingleton();
}
return LAZY_SINGLETON;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() ->
System.out.println(LAZY_SINGLETON.getInstance().hashCode())
).start();
}
}
}
执行结果
2003664945
2003664945
2003664945
2003664945
2003664945
2003664945
2003664945
2003664945
.
.
.
synchronized
关键字保证线程安全。package com.wzj.singleton;
/**
* @Author: wzj
* @Date: 2020/2/13 13:50
* @Desc: 懒汉式-线程不安全
* 试图通过减小同步代码块的方式提高效率,带来了线程不安全
*/
public class ThreadUnSafeSingleton {
private static ThreadUnSafeSingleton LAZY_SINGLETON;
private ThreadUnSafeSingleton() {
}
public static ThreadUnSafeSingleton getInstance() {
if (null == LAZY_SINGLETON) {
//试图通过减小同步代码块的方式提高效率,带来了线程不安全
synchronized (ThreadUnSafeSingleton.class) {
//睡眠1毫秒,在new对象前,增加被其他线程打断的机会,保证能被多个线程执行
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
LAZY_SINGLETON = new ThreadUnSafeSingleton();
}
}
return LAZY_SINGLETON;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() ->
System.out.println(LAZY_SINGLETON.getInstance().hashCode())
).start();
}
}
}
结果:
1276043710
1276043710
1276043710
1276043710
1276043710
1276043710
2116194883
2116194883
2116194883
2116194883
.
.
.
package com.wzj.singleton;
/**
* @Author: wzj
* @Date: 2020/2/13 13:50
* @Desc: 双重检测下线程安全
*
*/
public class DoubleCheckedThreadSafeSingleton {
private volatile static DoubleCheckedThreadSafeSingleton LAZY_SINGLETON;
private DoubleCheckedThreadSafeSingleton() {
}
public static DoubleCheckedThreadSafeSingleton getInstance() {
if (null == LAZY_SINGLETON) {
//试图通过减小同步代码块的方式提高效率,带来了线程不安全
synchronized (DoubleCheckedThreadSafeSingleton.class) {
if (null == LAZY_SINGLETON) {
//睡眠1毫秒,在new对象前,增加被其他线程打断的机会,保证能被多个线程执行
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
LAZY_SINGLETON = new DoubleCheckedThreadSafeSingleton();
}
}
}
return LAZY_SINGLETON;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() ->
System.out.println(LAZY_SINGLETON.getInstance().hashCode())
).start();
}
}
}
执行结果
527405373
527405373
527405373
527405373
527405373
527405373
527405373
527405373
.
.
.
if (null == LAZY_SINGLETON)
,用来检测如果内存中有单例生成以后,永不进入下面的代码,直接走return
语句返回已有的单例,第二个判空语句,保证当前线程拿到锁的前后,内存中都没有单例,才执行创建单例操作,防止中途被其他线程创建单例,进而重复创建;volatile
关键字保证在执行语句LAZY_SINGLETON = new DoubleCheckedThreadSafeSingleton()
时,可以分解为如下的3行伪代码。memory = allocate(); // 1:分配对象的内存空间
ctorInstance(memory); // 2:初始化对象
instance = memory; // 3:设置instance指向刚分配的内存地址
上面3行伪代码中的2和3之间,可能会被重排序(在一些JIT编译器上,这种重排序是真实发生的)。2和3之间重排序之后的执行时序如下。
memory = allocate(); // 1:分配对象的内存空间
instance = memory; // 3:设置instance指向刚分配的内存地址
// 注意,此时对象还没有被初始化!
ctorInstance(memory); // 2:初始化对象
上面的代码在编译器运行时,可能会出现重排序 从1-2-3 排序为1-3-2,如果发生重排序,另一个并发执行的线程B就有可能在判断instance不为null。线程B接下来将访问instance所引用的对象,但此时这个对象可能还没有被A线程初始化!
package com.wzj.singleton;
/**
* @Author: wzj
* @Date: 2020/2/13 15:45
* @Desc: 静态内部类方式
* JVM保证单例
* 加载外部类时不会加载内部类,这样可以实现懒加载
*/
public class InnerStaticClassSingleton {
private InnerStaticClassSingleton() {
}
//内部静态类
private static class InnerStaticClassSingletonHolder {
private static final InnerStaticClassSingleton INSTANCE = new InnerStaticClassSingleton();
}
public static InnerStaticClassSingleton getInstance() {
return InnerStaticClassSingletonHolder.INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() ->
System.out.println(InnerStaticClassSingleton.getInstance().hashCode())
).start();
}
}
}
执行结果:
772510047
772510047
772510047
772510047
772510047
772510047
772510047
772510047
772510047
772510047
.
.
.
package com.wzj.singleton;
/**
* @Author: wzj
* @Date: 2020/2/13 16:05
* @Desc: 枚举实现,既可以保证线程安全,还可以防止反序列化。
*/
public enum EnumSingleton {
INSTANCE;
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->{
System.out.println(EnumSingleton.INSTANCE.hashCode());
}).start();
}
}
}
以下源码分析基于Spring5.0.6 RELEASE。DefaultSingletonBeanRegistry.class
部分源码如下.
@Nullable
public Object getSingleton(String beanName) {
return this.getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从单例缓存容器中加载 bean
Object singletonObject = this.singletonObjects.get(beanName);
// 单例缓存容器中的 bean 为空,且当前 bean 正在创建
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
// 加锁
synchronized(this.singletonObjects) {
// 从 earlySingletonObjects 容器中获取
singletonObject = this.earlySingletonObjects.get(beanName);
// earlySingletonObjects容器中没有,且允许提前创建
if (singletonObject == null && allowEarlyReference) {
// 从 singletonFactories 中获取对应的 ObjectFactory
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
// ObjectFactory 不为空,则创建 bean
if (singletonFactory != null) {
// 获取 bean
singletonObject = singletonFactory.getObject();
// 添加 bean 到 earlySingletonObjects 中
this.earlySingletonObjects.put(beanName, singletonObject);
// 从 singletonFactories 中移除对应的 ObjectFactory
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
singletonObjects
中,获取 Bean 对象。bean
正在创建中,则从earlySingletonObjects
中获取Bean
对象。singletonFactories
中获取相应的ObjectFactory
对象。若不为空,则调用其ObjectFactory
的getObject(String name)
方法,创建Bean
对象,然后将其加入到earlySingletonObjects
,然后从singletonFactories
删除。Spring
在创建单例bean
的时候,采用的是双重检测加锁机制创建bean
的。JVM
退出,静态方法中的对象,会随着静态方法执行完被释放,gc
回收。单例的具体写法,需要结合场景与业务要求,确认是否支持线程安全,是否支持延迟加载,单例比较简单的写法是饿汉式,唯一的不足是不支持懒加载,还有静态内部类;比较完美的写法是双重检测加锁,虽然写法复杂,但支持延迟加载,线程安全,也是Spring源码使用的方式。
原文:https://www.cnblogs.com/father-of-little-pig/p/12304306.html