定义
采取一定的方法,保证整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
实现方式
1.饿汉式(静态常量)
- 构造器私有化
- 类的内部创建对象
- 向外暴露一个静态的公共方法
getInstance - 代码实现:
class Singleton {
private Singleton() {
}
private final static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
- 优缺点:
- 优点:简单了,类装载时候就完成了实例化,避免了线程同步问题
- 缺点:类装载时候就完成了实例化,没有达到懒加载效果;如果从未使用过这个实例,会造成内存浪费
2.饿汉式(静态代码块)
同一
class Singleton {
private Singleton() {
}
private static Singleton instance;
static {
//在静态代码块中创建单例对象
instance = new Singleton();
}
public static Singleton getInstance() {
return instance;
}
}
3.懒汉式(线程不安全)
- 起到了
Lazy loading作用,但是只能在单线程下使用 - 多线程下,一个线程进入了
if(singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例,导致线程不安全 - 开发中不要使用该方式
class Singleton {
private Singleton() {
}
private static Singleton instance;
public static Singleton getInstance() {
//使用到该方法时才去创建
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
4.懒汉式(线程安全,同步方法)
- 每次都要进行同步,实际上该方法只执行一次实例化代码就够了
class Singleton {
private Singleton() {
}
private static Singleton instance;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
5.懒汉式(线程不安全,同步代码块)
class Singleton {
private Singleton() {
}
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
6.双重检查常用
class Singleton {
private Singleton() {
}
/**
* volatile让修改对其他线程立即可见
*/
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
7.静态内部类常用
class Singleton{
private Singleton() {
}
/**
* 1.Singleton装载时,SingletonInstance不会被装载
* 2.调用getInstance时,内部类才会装载,类装载时线程安全
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
8.枚举常用
- 线程安全
- 能防止反序列化重新创建新的对象
enum Singleton{
/**
*定义一个枚举元素,代表了Singleton的一个实例
*/
INSTANCE;
public void operation() {
}
}
9.注册(map) 常用
class Singleton {
private Singleton() {
}
private final static String INSTANCE = "INSTANCE";
/**
* 要用ConcurrentHashMap
*/
private static Map<String, Singleton> map = new ConcurrentHashMap<>();
public static Singleton getInstance() {
Singleton singleton = map.get(INSTANCE);
if (singleton == null) {
singleton = new Singleton();
map.put(INSTANCE, singleton);
}
return singleton;
}
}
源码中使用情况
1.JDK
java.lang.Runtime
public Class Runtime{
//饿汉式
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
}
2.Spring
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
//double-check
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
反射下的单例
public class ReflectSingletonTest{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Singleton> clazz = Singleton.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance = Singleton.getInstance();
Singleton reflectInstance = (Singleton) constructor.newInstance();
System.out.println(instance == reflectInstance);
}
}
class Singleton {
private Singleton(){
//防止反射攻击, 不加上该段,上述运行结果为false
if (SingletonHolder.INSTANCE != null) {
throw new IllegalStateException();
}
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 如果不是在类加载的时候创建对象实例的这种单例,是没有办法防止反射攻击的(先反射获取对象,再正常获取对象的话,反射创建对象时是不会进入构造其中if代码块的)
序列化下的单例
1.序列化、反序列化会破坏单例
public class SeriaSingletonTest {
public static void main(String[] args) throws Exception {
Singleton instance = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/seria.txt"));
oos.writeObject(instance);
oos.flush();
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/seria.txt"));
Singleton instance2 = (Singleton) ois.readObject();
//返回false
System.out.println(instance == instance2);
}
}
class Singleton implements Serializable {
private Singleton() {
}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
2.解决办法
加上readResolve方法
class Singleton implements Serializable {
private Singleton() {
}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
/**
* 防止反序列化时单例被破坏
* @return
*/
private Object readResolve() {
return instance;
}
}
3.原理
java.io.ObjectStreamClass#invokeReadResolve
Object invokeReadResolve(Object obj)
throws IOException, UnsupportedOperationException
{
requireInitialized();
//1.有readResolve方法时进入
if (readResolveMethod != null) {
try {
//2.反射调用readResolve方法的返回值,即instance
return readResolveMethod.invoke(obj, (Object[]) null);
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException) th;
} else {
throwMiscException(th);
throw new InternalError(th); // never reached
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError(ex);
}
} else {
throw new UnsupportedOperationException();
}
}
相关代码 https://github.com/liberxk/blog-code/tree/master/desing-pattern
参考:
1.《研磨设计模式》
2.图解Java设计模式 韩顺平