介绍

单例模式时设计模式中最简单的一种设计模式,单例设计模式,就是采取一定的办法保证在整个软件中,对于某个类只有一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)

单例模式的优缺点:

  • 只有一个对象,内存开支少、性能好
  • 避免对资源的多重占用
  • 在系统设置全局访问点,优化和共享资源访问

饿汉式和懒汉式概述:

  • 饿汉式:类装载的时候就初始化
  • 懒汉式:需要使用这个实例的时候进行初始化

实现单例模式的七种方式

  1. 饿汉式(静态常量)
  2. 饿汉式(静态代码块)
  3. 懒汉式(线程不安全)
  4. 懒汉式(线程安全,同步方法)
  5. 双重检查(双检锁)
  6. 静态内部类
  7. 枚举

一、饿汉式(静态常量)

介绍

就像是一个很久没有吃饭的人,有吃的立马就吃掉(类加载完成后,就立马创建对象)

实现步骤:

  1. 构造器私有化(防止通过 new 创建对象)
  2. 类的内部创建对象
  3. 对外公布一个静态的公共方法

示例:

package singleton;

/**
 * @author zheng
 * @date 2021/12/13 22:14
 */
public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();

        // 比较对象是否相等,这里比较的是地址值
        System.out.println(singleton1 == singleton2);
        // Singleton 的 hashcode 值
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }
}

class Singleton {
    // 私有化后,无法通过new创建对象
    private Singleton() {
    }

    // 类加载完成立马创建对象
    private final static Singleton singleton = new Singleton();

    public static Singleton getSingleton() {
        return singleton;
    }
}

输出:

true
1239731077
1239731077

优缺点:

  • 优点:写法简单,在类装载的时候就完成了实例化,避免了线程同步问题
  • 缺点:在类装载的时候就完成了实例化,没有达到懒加载的效果,如果不是用这个实例就造成了内存浪费

二、饿汉式(静态代码块)

介绍:

这种方法和上面那种基本一致,只不过把类实例化的过程放到静态代码块当中

示例:

package singleton;

/**
 * @author zheng
 * @date 2021/12/13 22:14
 */
public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();

        // 比较对象是否相等,这里比较的是地址值
        System.out.println(singleton1 == singleton2);
        // Singleton 的 hashcode 值
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }
}

class Singleton {
    // 私有化后,无法通过new创建对象
    private Singleton() {
    }

    private static Singleton singleton;

    static {
        singleton = new Singleton();
    }

    public static Singleton getSingleton() {
        return singleton;
    }
}

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

介绍:

这是一个很懒的人,只有当你叫他干活的时候,他才会干活(使用时创建实例)

示例:

package singleton;

/**
 * @author zheng
 * @date 2021/12/13 22:43
 */
public class SingletonTest1 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();

        // 比较对象是否相等,这里比较的是地址值
        System.out.println(singleton1 == singleton2);
        // Singleton 的 hashcode 值
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }
}

class Singleton {
    // 私有化后,无法通过new创建对象
    private Singleton() {
    }

    private static Singleton singleton;

    public static Singleton getSingleton() {
        // 这个有线程安全问题
        if (singleton == null) {
            // 线程1、线程2可能同时进入到这里,创建实例,造成内存浪费
            singleton = new Singleton();
        }
        return singleton;
    }
}

优缺点:

  • 优点:起到了懒加载的作用
  • 缺点:只能在单线程的环境下使用,一个线程进入 if (singleton == null)  判断语句块,还没来的及往下执行,另一个线程也通过了这个判断语句,这时就会产生多个实例

不推荐这种方式

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

package singleton;

/**
 * @author zheng
 * @date 2021/12/13 22:43
 */
public class SingletonTest1 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();

        // 比较对象是否相等,这里比较的是地址值
        System.out.println(singleton1 == singleton2);
        // Singleton 的 hashcode 值
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }
}

class Singleton {
    // 私有化后,无法通过new创建对象
    private Singleton() {
    }

    private static Singleton singleton;

    // synchronized 关键字是方法变成同步的,解决线程安全问题
    public static synchronized Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

优缺点:

  • 优点:解决了线程安全问题
  • 缺点:由于方法被 synchronized 关键字修饰,使方法变为同步,导致效率大幅度降低

不推荐使用这种方式

五、双重检查

package singleton;

/**
 * @author zheng
 * @date 2021/12/13 22:43
 */
public class SingletonTest1 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();

        // 比较对象是否相等,这里比较的是地址值
        System.out.println(singleton1 == singleton2);
        // Singleton 的 hashcode 值
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }
}

class Singleton {
    // 私有化后,无法通过new创建对象
    private Singleton() {
    }

    private static volatile Singleton singleton;
    
    // 提供了一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题,并且保证了效率
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

优缺点:

  • 优点:线程安全,延迟加载,效率较高

在实际开发中,推荐这种方式实现单例模式

六、静态内部类

示例:

/**
 * @author zheng
 * @date 2021/12/13 22:43
 */
public class SingletonTest1 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();

        // 比较对象是否相等,这里比较的是地址值
        System.out.println(singleton1 == singleton2);
        // Singleton 的 hashcode 值
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }
}

class Singleton {
    // 私有化后,无法通过new创建对象
    private Singleton() {
    }
    
    /**
     * 静态内部类在 Singleton 被装载时并不会立即实例化,而是在使用的时候才会实例化
     * 当调用 getSingleton() 方法时才会装在 SingletonInstance 这个静态内部类
     */
    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getSingleton() {
        return SingletonInstance.INSTANCE;
    }
}

优缺点:

  • 优点:避免了线程不安全,利用静态内部类的特点实现延迟加载,效率搞

推荐使用这种方式

七、枚举

示例:

/**
 * @author zheng
 * @date 2021/12/13 22:43
 */
public class SingletonTest1 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        // 比较对象是否相等,这里比较的是地址值
        System.out.println(singleton1 == singleton2);
        // Singleton 的 hashcode 值
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());

    }
}

class Singleton {
    public Singleton() {
    }

    /**
     * 枚举类型时线程安全的并且只会装载一次
     */
    private enum SingletonEnum {
        INSTANCE;
        private final Singleton instance;

        SingletonEnum() {
            instance = new Singleton();
        }

        private Singleton getInstance() {
            return instance;
        }
    }

    /**
     * 枚举类实现单例模式是 effective java 作者极力推荐的单例实现模式
     * 因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式
     * 枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
     */
    public static Singleton getInstance() {
        return SingletonEnum.INSTANCE.getInstance();
    }
}

优缺点:

  • 优点:避免了多线程同步问题,而且还能防止反序列化重新创建新的对象

非常推荐

本篇文章就先分享到这里了,如果有问题欢迎评论区留言提问!


版权声明:本文为m0_46437589原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_46437589/article/details/121915332