浅色年华的gravatar头像
浅色年华 2017-08-01 11:53:44
单例模式介绍及优化

什么是单例模式?

单例模式就是保证系统中一个类只有一个实例。即一个类只有一个对象实例

特点:
1,一个类只能有一个实例
2,自己创建这个实例
3,整个系统都要使用这个实例

他有两种模式,

1.懒汉模式:一开始不产生实例,当调用的时候判断是否有这个类的实例,如果没有采取创建

public class SingletonClass{
    private static SingletonClass instance=null;
    public static synchronized SingletonClass getInstance(){
        if(instance==null){
               instance=new SingletonClass();
        }
        return instance;
    }
    private SingletonClass(){
    }
}

2.饿汉模式:类加载的时候就生成这个类的实例,以后调用直接返回这个类的实例

public class Singleton{
    //在自己内部定义自己的一个实例,只供内部调用
    private static final Singleton instance = new Singleton();
    private Singleton(){
        //do something
    }
    //这里提供了一个供外部访问本class的静态方法,可以直接访问
    public static Singleton getInstance(){
        return instance;
    }
}

使用注意事项: 
    1.使用时不能用反射模式创建单例,否则会实例化一个新的对象 
    2.使用懒单例模式时注意线程安全问题 
    3.饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式) 
适用场景: 
    单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如: 
    1.需要频繁实例化然后销毁的对象。 
    2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
    3.有状态的工具类对象。 
    4.频繁访问数据库或文件的对象。 

    5.生成系统主键
以下都是单例模式的经典使用场景: 
    1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 
    2.控制资源的情况下,方便资源之间的互相通信。如线程池等。 

优化:

当使用懒汉模式时我们本来只是想让new这个操作并行就可以了,现在,只要是进入 getInstance()的线程都得同步啊,注意,创建对象的动作只有一次,后面的动作全是读取那个成员变量,这些读取的动作不需要线程同步啊。这样的 作法感觉非常极端啊,为了一个初始化的创建动作,居然让我们达上了所有的读操作,严重影响后续的性能啊!

我们想办法在进行线程同步之前在检查一下是否初产生了实例,修改懒汉模式如下

public class Singleton {

    private static Singleton instance;

    private Singleton(){}

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

1. 第一个条件是说,如果实例创建了,那就不需要同步了,直接返回就好了。
2. 不然,我们就开始同步线程。
3. 第二个条件是说,如果被同步的线程中,有一个线程创建了对象,那么别的线程就不用再创建了。

这种修改后的懒汉模式也叫“双重检查”,这还不是最好的解决方案,可以使用内部静态类来解决。

由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。

      通过使用IoDH,我们既可以实现延迟加载,又可以保证线程安全,不影响系统性能,不失为一种最好的Java语言单例模式实现方式(其缺点是与编程语言本身的特性相关,很多面向对象语言不支持IoDH)

代码如下

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

打赏
最近浏览
chiaki 2020年3月10日
暂无贡献等级
浅色年华  LV5 2017年8月1日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友