设计模式-单例模式

        单例模式,是在实际工作和面试中比较常见的模式,下面我们来学习单例模式的几种实现方法。

一、定义

        首先看定义,单例模式:保证在软件系统中,对于某个类,只有一个对象实例,并且只提供一种获取该类实例的方法。

二、饿汉式

        饿汉式是一种常见且直观的单例实现。实现步骤:

1、构造器私有化,防止外部new;

2、本类内部创建对象实例;

3、提供一个公有的静态方法,返回对象实例;

        实现代码:

public class SingletonTest1 {

    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton1 == singleton2);
        System.out.println("singleton1:" + singleton1.hashCode());
        System.out.println("singleton2:" + singleton2.hashCode());
    }
}
// 饿汉式
class Singleton {
    // 1、构造器私有化,防止外部能new
    private Singleton() { }

    // 2、本类内部创建对象实例,私有静态变量
    private final static  Singleton instance = new Singleton();

    // 3、提供一个外部调用实例的静态方法
    public static Singleton getInstance() {
        return instance;
    }
}

输出:

        根据输出的哈希值,可以看见两次获取的是同一个对象。

        饿汉式的优缺点:

1、类加载的时候就完成了创建实例,避免线程同步问题;

2、从未使用该类也会被实例化,造成内存浪费;

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

        上面说到的饿汉式实现,当这个类不是常用类时,会有个内存浪费的问题。我们可以进行优化,当使用到这个类时,才进行类的实例化。也就是延迟创建对象,即懒汉式。

        实现代码:

// 懒汉式(线程不安全)
class Singleton {
    // 1、构造器私有化,防止外部能new
    private Singleton() { }

    // 2、私有实例变量
    private static Singleton instance;

    // 3、提供一个外部调用实例的静态方法,当使用到时,才实例化对象
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

        上面的代码实现了延迟创建对象,但存在多线程安全问题。当多个线程访问时,可能会同时进入到if(instance == null)方法内,导致实例化出多个对象。

四、懒汉式(线程安全)

        解决线程安全问题,第一个想到的是加synchronized。

        实现代码:

// 懒汉式(线程安全)
class Singleton {
    // 1、构造器私有化,防止外部能new
    private Singleton() { }

    // 2、私有实例变量
    private static Singleton instance;

    // 3、提供一个外部调用实例的静态方法,当使用到时,才实例化对象。并用synchronized解决同步问题。
    public synchronized static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

        使用synchronized很好的解决了同步问题。但思考下,synchronized是加在方法上的,每一次的访问都要进行同步阻塞,影响效率。其实只需要在第一次初始化时,才需要同步。

五、双重检查

        上面的懒汉式,虽然延迟加载,线程安全,但效率不高,不是理想的实现方式。下面继续优化,用双重检查实现。

        实现代码:

// 双重检查,推荐使用
class Singleton {
    // 1、构造器私有化,防止外部能new
    private Singleton() { }

    // 2、私有实例变量,增加volatile,读取最后修改的值
    private static volatile Singleton instance;

    // 3、提供一个外部调用实例的静态方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
    public static Singleton getInstance() {
        if(instance == null) {
            synchronized(Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

        双重检查是我们工作中推荐的单例实现方式。优点:

1、双重检查是多线程开发中经常使用的,如代码所示,进行了两次instance非空判断,并用volatile修饰instance,保证了线程安全。

2、实例化代码只执行了一次,而且不会重复访问同步方法块,效率高。

3、达到延迟加载的效果。

六、静态内部类

        扩展,推荐的方式除了双重检查,还有种实现方式——静态内部类。

1、当外部类被装载时,静态内部类不会被装载,达到延迟加载的效果;

2、当获取静态内部类属性时,静态内部类属性只实例化一次,并且线程安全的;因为JVM类装载是线程安全的;

        实现代码:

// 静态内部类,推荐使用
class Singleton {
    // 1、构造器私有化,防止外部能new
    private Singleton() { }

    // 2、私有实例变量,增加volatile,读取最后修改的值
    private static volatile Singleton instance;

    // 3、静态内部类,属性是Singleton的实例
    private static class SingletonInstance {
        private static Singleton INSTANCE = new Singleton();
    }
    // 4、提供一个外部调用实例的静态方法
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

七、枚举

        继续扩展,另一种推荐实现——枚举。

1、利用枚举可以很轻松的实现单例模式,不仅能实现多线程安全,而且能防止反序列化对象被重新创建;

2、这种方式是effective java作者josh Bloch提倡的方式;

        实现代码:

public class SingletonTest6 {

    public static void main(String[] args) {
        Singleton singleton1 = Singleton.INSTANCE;
        Singleton singleton2 = Singleton.INSTANCE;
        System.out.println(singleton1 == singleton2);
        System.out.println("singleton1:" + singleton1.hashCode());
        System.out.println("singleton2:" + singleton2.hashCode());
    }
}

// 枚举,推荐使用
enum Singleton {
    INSTANCE;
}

        以上就是单例模式的常见实现方法了。欢迎讨论,共同学习。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值