Java 单例模式

在常用的设计模式中,只能生成一个实例的类是实现了singleton模式的类型。单例模式在面向对象程序设计中有着举足轻重的作用。这里我们来看看单例模式的特点:

1)单例只能有一个实例。

2)单例必须自己创建一个自己的实例

3)单例需要给其他对象提供这个实例

单例模式的主要有两种写法,懒汉式和饿汉式。

1.懒汉式

    懒汉式,即只有在需要用到这个实例的时候才创建这个实例。由于单例模式要求只能生成一个实例,所以我们必须把构造函数设为私有函数以防止他人创建该实例,我们可以定义一个静态的实例,然后让他在需要的时候创建该实例。下面定义的类型singleton1就是基于这个思路的实现:

public class Singleton {
    private Singleton(){
    }
    private static Singleton Instance = null;
    //静态方法
    public static Singleton getInstance(){
        if(Instance==null){
            Instance = new Singleton();
        }
        return Instance;
    }
}

    上述代码中,只有在Intance为null的时候才创建一个实例以避免重复创建,同时我们把构造函数定义为私有函数,这样就能确保只创建一个实例。

    如果只是单线程的时候上述代码是完全没问题的,但如果是多线程的情况下呢,设想如果两个线程同时运行到判断Intance是否为null的if条件语句,并且Intance的确为null,那么两个线程都会创建一个实例,此时类型Singleton就不满足单利模式的要求了。为了保证在多线程的环境下我们还能得到类型的唯一实例。我们需要加上一个同步锁,把Singleton做修改得到如下代码:

getIntance方法上加锁

public class Singleton2 {
    private Singleton2(){
    }
    private static Singleton2 Intance = null;
    public static synchronized Singleton2 getIntance(){
        if(Intance==null){
            Intance = new Singleton2();
        }
        return Intance;
    }
}

    上述代码中,我们看到,在两个线程同时想创建一个实例时,由于我们加了同步锁,由于同一时刻只有一个线程能得到同步锁,所以当第一个线程加上锁时,第二个线程只能等待。当第一个线程发现实例还没有被创建时,他会创建一个实例,然后释放同步锁。此时第二个线程可以加上同步锁,并运行接下来的代码。这个时候由于实例已经被第一个线程所创建,所以第二个线程就不会再创建实例了,这压根我们就可以在多线程的环境中也只能得到一个实例。

    但是类型Singeleton2还不是很完美,因为我们每次通过通过属性Intance得到Singleton2的实力是都会试图加上一个同步锁,而加锁是一个非常耗时的操作,所以在没有必要的时候我们应该尽量避免。分析一下,其实只有实例没有被创建之前我们需要加锁来保证只有一个线程创建出实例。而当实例创建后我们就已经不需要再进行加锁操作了。于是我们将解法2中的代码作进一步优化:

双重检查加锁

public class Singleton3 {
    private Singleton3(){
    }
    private static Singleton3 Intance = null;
    public static Singleton3 getIntance(){
        if (Intance==null){
            synchronized (Singleton3.class){
                if(Intance==null){
                    Intance = new Singleton3();
                }
            }
        }
        return Intance;
    }
}

    上述代码中,只有当Intance为null即没有创建时需要加锁,当Intance创建出来后就不需要再加锁了。因为只有第一次的时候Intance才为null;这样在时间效率上,比singleton2要好很多。

 

上述代码中确实很好的解决了时间效率的问题,但是代码的复杂程度增加了,还有没有更好的方法呢。我们可以考虑利用java静态内部类的方法实现:代码如下:

public class Singleton4 {
    private Singleton4(){
    }
    private static class NewIntance{
        private static Singleton4 Intance = new Singleton4();
    }
    public static Singleton4 getIntance(){
       return NewIntance.Intance;
    }
}

    在上述代码中,我们在内部定义了一个私有类型NewIntace,当第一次用到这个嵌套类型时,会调用静态函数创建Singleton4的实力Intance,类NewIntace只有在属性Singleton。getIntance里会被用到。其私有属性使其无法被他人使用。这种方法比起上三种来说,既实现了线程安全,又不影响效率。

2.饿汉式

       饿汉式即只要类装载完成,便创建这个实例,不管是否需要调用到。这种方法利用java的静态构造函数的特性实现。所以其天生线程安全,但是可能过早的创建实例,会降低内存的使用效率,代码如下:

public class Singleton1 {
    private Singleton1(){
    }
    private static final Singleton1 Intance = new Singleton1();
    public static Singleton1 getIntance(){
        return Intance;
    }
}

 

通过以上方法的示例和分析后,我们发现利用静态内部类实现单例模式是最适合的方法了。

 

 

 

 

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页