码迷,mamicode.com
首页 > 其他好文 > 详细

【设计模式】单例模式

时间:2019-09-10 17:44:15      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:并且   error   asi   string   stat   线程同步   wear   loading   sed   

定义

采取一定的方法,保证整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法

实现方式

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设计模式 韩顺平

【设计模式】单例模式

标签:并且   error   asi   string   stat   线程同步   wear   loading   sed   

原文地址:https://www.cnblogs.com/liberxk/p/11440116.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!