欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

单例模式(Singleton)

程序员文章站 2022-06-28 16:53:51
单例模式(Singleton) 确保某个类只有一个实例,并且自行实例化并向整个系统提供这个单例。 单例对象能保证在一个JVM中,该对象只有一个实例存在。好处如下: (1)某些类的创建比较频繁,对于一些大型的对象,系统开销很大; (2)省去了new操作符,降低了系统内存的使用频率,减轻GC压力; 单例 ......

单例模式(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] 单例模式

http://www.cnblogs.com/cbf4life/archive/2009/12/18/1626850.html

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

http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html