浅谈Java设计模式之单例模式

在生活中我们经常会遇到这种情况:只能有一个对象存在。例如一个团队中只能有一个最高领导者、在windows系统中只能有一个资源管理器弹窗,这就需要使用单例模式来实现。

单例模式使我们在开发过程中最常用、最简单的设计模式,属于创建型模式,这种模式确保在整个程序中只能存在一个实例。

使用单例模式,增加了对象的复用率,减少了存储的压力,是一种最佳创建对象的方法。

单例模式的关键代码主要有三个点:

1、单例类只能有一个实例,可以通过同步或类加载原理实现;

2、单例类必须自己创建唯一的实例。所以需要将构造方法私有化;

3、单例类必须提供一个获取单例实例的方法。

本篇文章主要介绍6中常用单例。

1、懒汉式(线程不安全)

/**
 * 懒汉式 线程不安全
 * @author 樱桃肉丸子
 */
public class SinglePattern1 {

    /**
     * 将单例静态化、私有化
     */
    private static SinglePattern1 instance;

    /**
     * 私有化构造方法
     */
    private SinglePattern1(){}

    /**
     * 提供获取唯一实例方法
     * @return
     */
    public static SinglePattern1 getInstance(){
        if(instance != null){
            return instance;
        }
        return new SinglePattern1();
    }

}

所谓懒汉式就是在需要用到实例的时候才会实例化单例对象。这种方式是最简单的单例模式,但当多个线程进行访问的时候,并没有做到真正的实例,所以在开发过程中不推荐该方法。

2、懒汉式(线程安全)

/**
 * 懒汉式 线程安全
 * @author 樱桃肉丸子
 */
public class SinglePattern2 {

    /**
     * 单例私有化、静态化
     */
    private static SinglePattern2 instance;

    /**
     * 私有化构造方法
     */
    private SinglePattern2(){}

    /**
     * 提供获取唯一实例方法 同时加锁
     * @return
     */
    public static synchronized SinglePattern2 getInstance(){
        if(instance == null){
            return new SinglePattern2();
        }
        return  instance;
    }
}

这种方法是懒汉式的另一种实现,通过同步关键字来修饰保证线程安全。但加锁会影响到性能,所以在并发量较大的情况下不推荐使用该方法。

3、饿汉式

/**
 * 饿汉式
 * @author 樱桃肉丸子
 */
public class SinglePattern3 {

    /**
     * 私有化、静态化实例
     */
    private static SinglePattern3 instance = new SinglePattern3();

    /**
     * 私有化构造方法
     */
    private SinglePattern3 (){}

    /**
     * 提供获取唯一实例方法
     * @return
     */
    public static SinglePattern3 getInstance(){
        return instance;
    }
}

所谓饿汉式就是在类加载的时候就创建了实例,同时也是线程安全的。与上一种方式不同的是,这种方法通过类加载原理实现整个程序中只有这一个实例,展开说就是静态变量只会在类加载的时候进行声明和实例化。但这种方式也存在缺点,即在类加载的时候就会实例化但该对象并不会用到,所以占用了部分内存。在开发过程中,最经常用到的就是这种方法。

4、双检索式

/**
 * 双检索式
 * @author 樱桃肉丸子
 */
public class SinglePattern4 {
    /**
     * 单例静态化,同时用volatile修饰保证所有线程读取的是一个实例
     */
    private volatile static SinglePattern4 singleton;

    /**
     * 私有化构造方法
     */
    private SinglePattern4 (){}

    /**
     * 提供获取唯一实例方法
     * @return
     */
    public static SinglePattern4 getInstance() {
        if (singleton == null) {
            synchronized (SinglePattern4.class) {
                if (singleton == null) {
                    singleton = new SinglePattern4();
                }
            }
        }
        return singleton;
    }
}

双检索式是第二种和第三种的结合体,在线程安全的情况下依然保持着高性能。

5、静态内部类式

/**
 * 静态内部类式
 * @author 樱桃肉丸子
 */
public class SinglePattern5 {

    /**
     * 定义私有静态内部类
     */
    private static class SingletonHolder {
        private static final SinglePattern5 INSTANCE = new SinglePattern5();
    }

    /**
     * 私有化构造方法
     */
    private SinglePattern5 (){}

    /**
     * 提供获取唯一实例方法
     * @return
     */
    public static final SinglePattern5 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

这种方式使用静态内部类来实现,也是使用类加载的原理实现的线程安全,但与第三种不同的是,这种方式在类加载的时候并不会立刻创建单例,属于懒加载。所以这种方式相对于第三种方式来讲更合理。

6、枚举

/**
 * 枚举
 * @author 樱桃肉丸子
 */
public enum SinglePattern6 {
    INSTANCE;
    public void whateverMethod() {
    }
}

这是实现单例模式的最佳方法,支持序列化,但这种模式还没有完全被普及,在工作过程中其实很少用到。

总结:

一般情况下不推荐使用懒汉式单例,推荐使用饿汉式单例。如果有明确的懒加载需求,可以使用静态内部类方式实现,若涉及到序列化等问题可以使用枚举方式,其他特殊需求可以考虑双检索方式。


猜你喜欢

转载自blog.51cto.com/13593129/2287034