美文网首页
单例模式详解

单例模式详解

作者: 飞奔的小马 | 来源:发表于2018-04-03 17:26 被阅读15次
大纲.png
一.前言

在日常开发中或者在面试中用到的设计模式最多的就是单例模式,这篇文章简单的讲一下单例,包括单例的概念,好处,特点,几种写法及怎样防止攻击单例。

二.概念

单例模式是一种对象创建模式,它用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例

三.好处

对于频繁使用的对象,可以省略创建对象的时间,由于new操作的次数减少,因而对系统内存的使用频率也会降低,将减轻GC压力,缩短GC停顿时间。

四.特点

(1)单例类只有一个实例
(2)单例类必须只能自己创建自己的唯一实例
(3)单例类必须给所有其他对象提供这一实例

五.几种写法和特点

饿汉式

public class Singleton{
   private Singleton(){}
   private static Singleton single =new Singleton();
   public static Singleton getInstance(){
       return single;
   }
}

特点:在类加载初始化时就创建好一个静态对象供外部使用,线程安全,缺点是不能延迟加载
改善:懒汉式

懒汉式

public class Singleton{
    private Singleton(){}
    private static Singleton single =null;
    public static Singleton getInstance(){
        if(single = null){
            single = new Singleton();
        }
        return single;
    }
}

特点:虽然采用延迟加载方式实现,但在多线程并发下无法保证实例唯一。
改善:懒汉线程安全
使用synchronized同步锁

public class Single{
    private Singleton(){}
    private static Singleton =null;
    public static Singleton getInstance(){
        synchronized(Singleton.class){
           if(single ==null){
             single = new Singleton();
           }
        }
        return single;
    }
}

特点:虽然解决了多实例问题,但是运行效率低下,下一个线程想要获取对象,须等待上一个线程释放锁之后,才可以运行。解决DCL

双重检查锁

public class Singleton{
  private Singleton(){}
  private static Singleton single =null;
  public static Singleton getInstance(){
      if(single == null){
         synchronized (Singleton.class){
           if(single ==null){
             single = new Singleton();
           }
         }
      }
      return single;
  }
}

特点:避免整个方法都被锁,只对需要锁的代码部分加锁,可以提高执行效率。多线程的情况下可能发生JVM即时编译指令重排序 解决:volatile 优化:静态内部类,枚举

指令流水线是并行工作,多个指令可以同时处于同一个阶段,只要CPU内部相应的处理部件未被占满即可。这样乱序就可能产生了,比如两条访存指令,可能由于第二条指令命中了cache而导致它先于第一条指令完成。

静态内部类

public class Singleton{
    private static class SingletonHolder{
        private static final Singleton instance =new Singleton();
    }
    private Singleton(){}
    public static final Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

利用了静态变量的唯一性
优点:延迟加载 JVM本身机制保证了线程安全,没有性能缺陷,因为final static

枚举

public enum EnumSingle{
   INSTANCE;
    public void doSomething(){}
}

优点:写法简单 线程安全 但占内存较高

六.怎样通过反射攻击单例

首先通过反射获取构造函数,然后调用setAccessible(true) 就可以调用私有的构造函数,如果要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。
具体做法:
可以设置一个标志位默认是false,当创建第一个实例的时候将标志位设置为true,创建第二个实例的时候抛出异常。
上面实现单例的几种方法中除了枚举类型外,其他方式都会被反射攻击,即使它的构造方法是私有的。

//以上面的静态内部类为例
//SingletonAttack为模拟攻击的类
public class SingletonAttack {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {

        Class<?> classType = Singleton01.class;
        Constructor<?> constructor =classType.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton01 singleton01 = (Singleton01)constructor.newInstance();
        Singleton01 singleton02 = Singleton01.getInstance();

        System.out.print(singleton01==singleton02);
    }
}

运行结果为false,即singleton01和single02是两个不同的实例。
解决:
SingletonAttack修改

private static boolean flag = false;//定义一个标志位,用于判断是否是第一次创建实例
    private static class SingletonHolder{
        private static final Singleton01 instance =new Singleton01();
    }
    private Singleton01(){
        synchronized (Singleton01.class){
            if(!flag){
                flag = !flag;//创建实例后就设置为true
            }else {//第二次创建实例就抛出运行时异常
                throw new RuntimeException("singleton attacked");
            }
        }
    }
    public static final Singleton01 getInstance(){
        return SingletonHolder.instance;
    }
Exception in thread "main" java.lang.ExceptionInInitializerError
    at table.fsp.com.javatest.Singleton01.getInstance(Singleton01.java:22)
    at table.fsp.com.javatest.SingletonReflectAttack.main(SingletonAttack.java:18)
Caused by: java.lang.RuntimeException: singleton attacked
    at table.fsp.com.javatest.Singleton01.<init>(Singleton01.java:17)
    at table.fsp.com.javatest.Singleton01.<init>(Singleton01.java:7)
    at table.fsp.com.javatest.Singleton01$SingletonHolder.<clinit>(Singleton01.java:10)
    ... 2 more

可以看到抛出运行时异常了,成功阻止攻击单例。

另外枚举类型也是可以阻止反射攻击的

public enum  Singleton01 {
    INSTANCE;
    public void doSomething(){}
}
public class SingletonAttack{
    public static void main(String[] args) throws InstantiationException,IllegalAccessException, IllegalArgumentException, InvocationTargetException,NoSuchMethodException, SecurityException {

        Class<?> classType = Singleton01.class;
        Constructor<?> constructor =classType.getDeclaredConstructor();
        constructor.setAccessible(true);
        constructor.newInstance();

    }
}

运行结果

Exception in thread "main" java.lang.NoSuchMethodException: table.fsp.com.javatest.Singleton01.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at table.fsp.com.javatest.SingletonReflectAttack.main(SingletonAttack.java:15)

从结果看也是抛出了异常,阻止了反射攻击。

七.总结

本文介绍了单例的概念,几种写法,特点及如何阻击通过反射攻击单例,欢迎一起探讨。

相关文章

  • 单例

    iOS单例模式iOS之单例模式初探iOS单例详解

  • 设计模式之单例模式详解

    设计模式之单例模式详解 单例模式写法大全,也许有你不知道的写法 导航 引言 什么是单例? 单例模式作用 单例模式的...

  • iOS 单例模式

    关于单例模式的详解,看完这几篇,就完全了然了。iOS 单例模式iOS中的单例模式iOS单例的写法

  • 单例模式

    单例模式及C++实现代码单例模式4种实现详解 c++11改进我们的模式之改进单例模式 单例模式(Singleton...

  • 单例模式安全之反射攻击

    单例模式安全之反射攻击 源码 单例模式这里就不谈了,什么是单例模式可参考七种Java单例模式详解,这里是关于单例模...

  • Java架构师课程

    Java架构班开学典礼 Spring中常用的设计模式概述及工厂模式详解 单例模式及原型模式单例模式及原型模式单例模...

  • 高并发编程-04-线程安全-单例模式详解

    单例模式详解 1,编写单例模式 饿汉式:不会存在线程安全的问题 public class Singleton1 {...

  • 单例模式

    转自单例模式详解 稍作修改 世间万物都有它的起源,那单利模式的诞生原因或者说情景是怎么样的呢?单例模式...

  • 单例模式详解

    一.前言 在日常开发中或者在面试中用到的设计模式最多的就是单例模式,这篇文章简单的讲一下单例,包括单例的概念,好处...

  • 单例模式详解

    什么是单例 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系...

网友评论

      本文标题:单例模式详解

      本文链接:https://www.haomeiwen.com/subject/gojchftx.html