单例模式的实现

Posted 洞拐洞拐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式的实现相关的知识,希望对你有一定的参考价值。

  单例模式就是是一个类仅可以创建一个对象,在java中实现主要有两种方式:饿汉式和懒汉式。

  先看两种方式共有的部分,因为是单例,所以构造方法必须是私有的private,而且必须提供一个对外界开放的获取对象的方法,该方法内部控制返回唯一的一个对象实例:

public class Singleton {

    //构造方法私有,阻断外界直接创建对象的方法
    private Singleton() {}
    
    //提供一个获取唯一对象实例的方法
    public static Singleton getInstance(){
        //...
    }
    
}

  以上是不管什么方式的实现,都得遵循的一些规定,下面就介绍懒汉式和饿汉式:

  饿汉式是单例类感觉自己很饥饿(将实例对象想象为吃的),不管有没有别的类跟我要实例类,我都要自己先生成一个,像下面的实现: 

public class Singleton {

    //构造方法私有,阻断外界直接创建对象的方法
    private Singleton() {}
    
    //内部持有一个类初始化时唯一创建的一个类实例对象
    private static Singleton singleton = new Singleton();
    
    //提供一个获取唯一对象实例的方法
    public static Singleton getInstance(){
        return singleton;
    }
    
}

  饿汉式的有点在于编码逻辑简单好理解,无线程安全问题;缺点嘛,自然就是当我仅仅是想用Singleton类其他方法,他还是创建了一个对我现在来说没用的实例对象,当系统中这种饿汉式的单例类多起来的时候,无疑是一种资源浪费,这个问题正好懒汉式可以解决。

  懒汉式,就是自己首先持有一个空的单例类的实例,但是不会类一加载就创建实例,只有当别人第一次要我的实例对象我才给他创建,懒嘛,要是没人要我就不创建了:

public class Singleton {

    //构造方法私有,阻断外界直接创建对象的方法
    private Singleton() {}
    
    //内部持有一个单例类的引用
    private static Singleton singleton = null;
    
    //提供一个获取唯一对象实例的方法
    public static Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
    
}

  饿汉式的有点不用说了,就是解决了懒汉式的缺点;但是他的缺点就是多线程环境下,不尽人意啊,比如两个线程同时都是第一次去获取Singleton类的实例的时候,又同时执行到if(singleton==null)这一行,两个线程就会同时进入if语句执行体中去,可能先后执行了singleton = new Singleton();这就违反了单例模式的概念,所以还得想个办法解决这个问题。

  线程安全的饿汉式:(其实只要将if语句上加上线程锁,就可以避免两个线程一起跑到这个地方来了)

public class Singleton {

    //构造方法私有,阻断外界直接创建对象的方法
    private Singleton() {}
    
    //内部持有一个单例类的引用
    private static Singleton singleton = null;
    
    //提供一个获取唯一对象实例的方法
    public static Singleton getInstance(){
        synchronized (Singleton.class) {
            if(singleton==null){
                singleton = new Singleton();
            }
        }
        return singleton;
    }
    
}

  踏哒~,解决了,但是!你发现了么?这样的话,每次别人想获取sinleton实例的时候都得等待别的线程释放锁,自己再加锁,自己再释放锁,而这些锁的操作又是那么消耗时间,能不能再优化一下。想一想,是不是第一次访问完了,只要sington对象实例,不为空,直接返回就是了,没有现成问题啊,应该这样:

public class Singleton {

    //构造方法私有,阻断外界直接创建对象的方法
    private Singleton() {}
    
    //内部持有一个单例类的引用
    private static Singleton singleton = null;
    
    //提供一个获取唯一对象实例的方法
    public static Singleton getInstance(){
        if(singleton==null){
            synchronized (Singleton.class) {
                if(singleton==null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
    
}

  完美!线程安全的懒汉式,既解决了资源浪费问题,又兼顾了线程安全问题。

  不过还有个更巧妙地方法,这个就涉及到内部类的只是,当一个类加载的时候,其内部类并不加载,而是只要第一次用到内部类的时候内部类才会加载,而且如果这个内部类是static内部类,也就是说加载了这个类一次,以后就直接获取他就可以了,详细参见另一篇博客:http://www.cnblogs.com/WreckBear/p/5812942.html

public class Singleton {

    //构造方法私有,阻断外界直接创建对象的方法
    private Singleton() {}
    
    /*
     * 搭建一个内部静态类,外部类加载的时候,内部类并不会加载,
     * 只有当内部类被访问的时候才会被加载、初始化,加载之后就会一直保存在内存中
     */
    public static class Get{
        public static Singleton singleton = new Singleton();
    }
    
}

  在Main中获取:

public class Main {

    public static void main(String[] args) {
        Singleton singleton = Singleton.Get.singleton;
    }
}

  这种方法显得更加优雅一点,至此,结束!

以上是关于单例模式的实现的主要内容,如果未能解决你的问题,请参考以下文章

单例设计模式 (代码实现)

常用代码片段

常用代码片段

NopCommerce源码架构学习-二单例模式实现代码分析

使用单例模式实现日志写入。附代码

设计模式之单例模式以及简单代码实现