定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
类图
具体实现
1、非线程安全实现
package com.yrs.singleton;
/**
* @Author: yangrusheng
* @Description: "懒汉式"实现,非线程安全的单例模式实现。
* @Date: Created in 9:21 2018/7/5
* @Modified By:
*/
public class NonThreadSecuritySingleton {
private static NonThreadSecuritySingleton singleton = null;
private NonThreadSecuritySingleton() {
}
/**
* 当线程一进入该方法,if语句判断singleton是null,然后进行实例化。在还没有实例化完时,线程二进入该方法,
* 这时singleton依然是null,线程二也会实例化一个新的对象,就会造成有多个对象,因此线程不安全。
* @return
*/
public static NonThreadSecuritySingleton getNonThreadSecuritySingleton() {
if (singleton == null) {
singleton = new NonThreadSecuritySingleton();
}
return singleton;
}
}
2、线程安全的”懒汉式”实现:
package com.yrs.singleton;
/**
* @Author: yangrusheng
* @Description: 线程安全的"懒汉式"单例模式实现
* @Date: Created in 13:54 2018/7/5
* @Modified By:
*/
public class SynchronizedMethodSingleton {
private static SynchronizedMethodSingleton singleton = null;
private SynchronizedMethodSingleton() {
// 防止通过反射的方式,调用构造方法实例化对象。
if (singleton == null) {
singleton = this;
} else {
throw new IllegalStateException("Already initialized.");
}
}
/**
* 在方法上加锁,能够保证同一时间只有一个线程访问,能够保证线程安全,但它的一个致命的缺点是性能问题。当singleton不为
* null时,也需要等待其他线程释放锁后才能访问getSingleton方法。对该方法的访问将变成串行,而且获取锁和释放锁都需要
* 一定的开销,因此会出现性能问题。改进方案双重校验锁。
* @return
*/
public synchronized static SynchronizedMethodSingleton getSingleton() {
if (singleton == null) {
singleton = new SynchronizedMethodSingleton();
}
return singleton;
}
}
3、双重校验锁方式:
package com.yrs.singleton;
/**
* @Author: yangrusheng
* @Description: 双重校验锁方式,线程安全的单例模式实现。
* @Date: Created in 17:23 2018/7/5
* @Modified By:
*/
public class DoubleCheckLockSingleton {
// volatile关键字保证singleton = new DoubleCheckLockSingleton();语句在编译时不被指令重排序优化:
// 1、给 instance 分配内存 2、调用 Singleton 的构造函数来初始化成员变量
// 3、将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
// 会存在1-3-2的执行顺序,线程2在线程1进行到3时进入getSingleton方法中,就会得到一个未初始化成员变量的对象,之后的
// 操作会出现问题。
private volatile static DoubleCheckLockSingleton singleton;
//私有构造方法确保不被通过new实例化出对象
private DoubleCheckLockSingleton() {
// 防止通过反射的方式,调用构造方法实例化对象。
if (singleton == null) {
singleton = this;
} else {
throw new IllegalStateException("Already initialized.");
}
}
/**
* 双重校验是否为null的原因是:多个线程同时通过了第一个if语句,线程1获得锁,进行初始化后singleton不为null,线程1
* 释放锁,线程2获得锁后,如果没有再次校验singleton为null的话,线程2将会创建第二个DoubleCheckLockSingleton对象,就
* 会造成线程不安全。
* @return
*/
public static DoubleCheckLockSingleton getSingleton() {
// local variable increases performance by 25 percent
// Joshua Bloch "Effective Java, Second Edition", p. 283-284
DoubleCheckLockSingleton result = singleton;
if (result == null) {
synchronized (DoubleCheckLockSingleton.class) {
result = singleton;
if (result == null) {
result = singleton = new DoubleCheckLockSingleton();
}
}
}
return result;
}
}
4、饿汉式实现单例
package com.yrs.singleton;
/**
* @Author: yangrusheng
* @Description:
* @Date: Created in 15:30 2018/7/2
* @Modified By: 饿汉式单例,在类加载时会初始化静态变量,因此不会产生多个实例,也就不存在线程安全问题
*/
public class HungrySingleton {
private static final HungrySingleton singleton = new HungrySingleton();
private HungrySingleton() {
// 防止通过反射的方式,调用构造方法实例化对象。
if (singleton != null) {
throw new IllegalStateException("Already initialized.");
}
}
public static HungrySingleton getSingleton() {
return singleton;
}
public static void doSomething() {
}
}
5、饿汉式变形,静态代码块初始化
package com.yrs.singleton;
/**
* @Author: yangrusheng
* @Description: 饿汉式实现变形,静态代码块初始化
* @Date: Created in 8:14 2018/7/17
* @Modified By:
*/
public class StaticHungrySingleton {
private static StaticHungrySingleton singleton = null;
static {
singleton = new StaticHungrySingleton();
}
private StaticHungrySingleton() {
// 防止通过反射的方式,调用构造方法实例化对象。
if (singleton != null) {
throw new IllegalStateException("Already initialized.");
}
}
public static StaticHungrySingleton getSingleton() {
return singleton;
}
public static void doSomething() {
}
}
6、静态内部类实现
package com.yrs.singleton;
/**
* @Author: yangrusheng
* @Description: 静态内部类实现单例模式。懒加载,jvm保证线程安全。
* @Date: Created in 10:01 2018/7/6
* @Modified By:
*/
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {
// 防止通过反射的方式,调用构造方法实例化对象。
if (SingletonHolder.INSTANCE != null) {
throw new IllegalStateException("Already initialized.");
}
}
public static StaticInnerClassSingleton getSingleton() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
}
7、枚举类实现
package com.yrs.singleton;
/**
* @Author: yangrusheng
* @Description: 使用枚举来实现单例模式。线程安全,能够防止通过序列化或者反射的方式多次实例化。
* 单元素的枚举类型已经成为实现Singleton的最佳方法。
* @Date: Created in 10:59 2018/7/6
* @Modified By:
*/
public enum EnumSingleton {
SINGLETON;
@Override
public String toString() {
return getDeclaringClass().getCanonicalName() + "@" + hashCode();
}
public static void main(String[] args) {
System.out.println(EnumSingleton.SINGLETON.toString());
}
}
8、防止通过序列化或者反射的方式破坏单例特性
package com.yrs.singleton;
import java.io.*;
/**
* @Author: yangrusheng
* @Description: 当单例类变为可序列化(Serializable)的, 为了维护并保证Singleton,必须声明所有实例域都是transient的,
* 并提供一个readResolve方法。
* @Date: Created in 14:38 2018/7/6
* @Modified By:
*/
public class SerializeHungrySingleton implements Serializable {
// 为了保证Singleton,必须声明所有实例域都是transient的。
private static final transient SerializeHungrySingleton singleton = new SerializeHungrySingleton();
//私有构造方法确保不被通过new实例化出对象
private SerializeHungrySingleton() {
// 防止通过反射的方式,调用构造方法实例化对象。
if (singleton != null) {
throw new IllegalStateException("Already initialized.");
}
}
public static SerializeHungrySingleton getSingleton() {
return singleton;
}
// readResolve方法是为了保护单例属性。
// 反序列化的时候会判断如果实现了serializable 或者 externalizable接口的类中又包含readResolve()方法的话,
// 会直接调用readResolve()方法来获取实例。
private Object readResolve() {
return singleton;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
SerializeHungrySingleton singleton = SerializeHungrySingleton.getSingleton();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(singleton);
//将对象从流中取出来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
SerializeHungrySingleton singleton1 = (SerializeHungrySingleton) ois.readObject();
System.out.println(singleton == singleton1);
}
}
优点:
* 内存中只有一个实例,减少内存开支
* 只生成一个实例,减少系统的性能开销
* 避免对资源的多重占用
* 优化和共享资源访问
缺点:
* 扩展困难,除非修改代码
* 不利于测试
* 与单一职责原则有冲突
参考:
1.《设计模式之禅 第二版》
2. http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/
3. https://github.com/iluwatar/java-design-patterns/tree/master/singleton/src/main/java/com/iluwatar/singleton