当前位置: 移动技术网 > IT编程>软件设计>设计模式 > 单例模式(Singleton)

单例模式(Singleton)

2018年02月09日  | 移动技术网IT编程  | 我要评论

单例模式(Singleton

  确保某个类只有一个实例,并且自行实例化并向整个系统提供这个单例。

单例对象能保证在一个JVM中,该对象只有一个实例存在。好处如下:

  (1)某些类的创建比较频繁,对于一些大型的对象,系统开销很大;

  (2)省去了new操作符,降低了系统内存的使用频率,减轻GC压力;

单例模式的使用场景

  在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现问题,则可采用单例模式,具体的场景如下:

  (1)要求生产唯一序列号的环境;

  (2)在整个项目中需要有访问一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用每次刷新都纪录到数据库中,使用单例模式保持计数器的值,并确保线程的安全的。

  (3)创建一个对象需要的资源过多,如要访问IO、访问数据库等资源。

  (4)需要定义大量静态常量和静态方法(如工具类)的环境,可以采用单例模式

 

饿汉式(单例实例在类装载的时候就创建)

/**
 * 饿汉式,单例实例在类装载的时候就创建
 *
 */
public class Singleton1 {
    //构造方法私有
    private Singleton1() {        
    }
    //在类装载时创建实例
    private static Singleton1 instance = new Singleton1();
    //获得实例
    public Singleton1 getInstance(){
        return instance;
    }
}

 

懒汉式(单例实例在第一次被使用时构建,延迟初始化。)

/**
 * 懒汉式,单例实例在第一次使用时再创建,延迟初始化
 *
 */
public class Singleton2 {
    //持有私有静态实例,防止被引用,赋值为null,实现延迟加载
    private static Singleton2 instance = null;
    //构造方法私有,防止被实例化
    private Singleton2() {        
    }
    //创建实例
    public static Singleton2 getInstance(){
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }
}

  这个类基本可以满足要求,但是在多线程的环境下,会出现问题,解决办法:对getInstance()方法加synchronized关键字。但是synchronized关键字锁住的是这个对象,会降低性能,每次调用getInstance()方法都要对对象上锁,只有在第一次创建对象的时候需要加锁,之后就不需要了。

instance = new Singleton();是分两步来执行的。JVM不保证这两个操作的先后顺序,可能JVM会为新的Singleton实例分配空间,直接赋值给instance成员,然后再初始化这个Singleton实例,这样就可能出错了。

  要想实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。但是这样会浪费一定的空间,这种实现方式会在类加载的时候就初始化对象,不管需不需要。

  类级内部类(静态内部类),加载外部类的时候,并不会同时加载其静态内部类,只有在发生调用的时候才会进行加载,加载的时候就会创建单例实例并返回,实现了延迟加载;至于同步问题,采用静态初始化器的方式。

 

实际情况是,单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载时,这个类的加载过程是线程互斥的。当我们第一次调用getInstance()的时候,JVM能够帮我们保证instance只被创建一次,会保证把赋值给instance的内存初始化完毕。

/**
 * 使用内部类维护单例的实现
 *
 */
public class Singleton3 {
    //构造方法私有,防止被实例化
    private Singleton3() {        
    }
    //使用内部类来维护实例
    private static class SingletonFactory{
        private static Singleton3 instance = new Singleton3();
    }
    //获取实例
    public Singleton3 getInstance(){
        return SingletonFactory.instance;
    }
    //如果对象被用于实例化,可以保证对象在序列化前后保持一致
    public Object readReaolve(){
        return getInstance();
    }
}

 

类的静态方法和单例的区别

  (1)静态类不能实现接口。(接口中不允许有static修饰的方法)

  (2)单例可以被延迟初始化,静态类一般在第一次加载时初始化。之所以延迟加载,是因为有些类比较庞大,延迟加载有助于提升性能。

  (3)单例类可以被继承,他的方法可以被重写,但是静态类内部方法都是static,无法被重写(只有非静态方法才有多态)。

  在Spring中,每个Bean都是默认单例的,优点是Spring容器可以管理这些Bean的生命期,决定什么时候创建出来,什么时候销毁,销毁的时候要如何处理等。

 

参考博客

[1] 单例模式

[2] Java开发中的23种设计模式详解(转)

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网