设计模式之单例模式

什么是单例模式

单例模式介绍

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
所谓类的单例模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(一般是静态方法)。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式保证了该类在系统内存中只存在一个对象,节省了系统资源,对于一些要频繁创建与销毁的对象,使用单例模式可以提高系统性能。

单例模式应用

许多时候,整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,显然,这种方式简化了在复杂环境下的配置管理。

使用场景

需要频繁的进行创建和销毁的对象、创建对象时耗时过多或者耗费资源过多(重量级对象)但又经常用到的对象、工具类对象、频繁访问数据库和文件的对象(比如数据源、session工厂等)

优点

1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
2、避免对资源的多重占用。

八种实现方式

饿汉式(静态常量)
饿汉式(静态代码块)
懒汉式(线程不安全)
懒汉式(线程安全,同步方法)
懒汉式(线程安全,同步代码块)
双重检查
静态内部类
枚举

饿汉式(静态常量)

关键点
构造器私有化(防止 外部通过new创建实例)
在类的内部创建对象
向外暴露一个静态的公共方法(getInstance)

代码实现

/**
 * @Description: 单例模式-饿汉式(静态常量)
 * @Author: atong
 * @Date: 2020-06-09
 * @Version:v1.0
 */
public class Singleton01 {
    public static void main(String[] args) {
        //测试
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);
        System.out.println(singleton.hashCode());
        System.out.println(singleton1.hashCode());

    }
}

class Singleton {
    //私有构造函数,方式外部通过new创建对象
    private Singleton () {

    }

    //类的内部创建对象
    private final static Singleton singleton = new Singleton();

    //对外暴露一个静态方法
    public static Singleton getInstance() {
        return singleton;
    }

}

优点
写法简单,在类装载的时候完成实例化。避免线程同步问题。
缺点
在类加载的时候就进行实例化,没有达到Lazy Loading的效果,如果从始至终没有使用过这个实例,则会造成内存浪费。
总结
可用,但可能造成内存浪费。

饿汉式(静态代码块)

关键点
构造器私有化(防止 外部通过new创建实例)
类内部声明变量
静态代码块中,创建单例对象
向外暴露一个静态的公共方法(getInstance)
代码实现

/**
 * @Description: Singleton02单例模式-饿汉式(静态代码块)
 * @Author: atong
 * @Date: 2020-06-10
 * @Version:v1.0
 */
public class Singleton02 {
    public static void main(String[] args) {
        //测试
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);
        System.out.println(singleton.hashCode());
        System.out.println(singleton1.hashCode());

    }
}
class Singleton {
    //私有构造函数,方式外部通过new创建对象
    private Singleton () {

    }

    //类的内部声明变量
    private static Singleton singleton;
    //在静态代码块中创建单例对象
    static {
        singleton = new Singleton();
    }

    //对外暴露一个静态方法
    public static Singleton getInstance() {
        return singleton;
    }

}

优点
类的实例化过程放到了静态代码块中,也是在类加载的时候初始化实例。优缺点同上。
缺点
优缺点同上
总结
可用,但可能造成内存浪费。

懒汉式(线程不安全)

关键点
构造器私有化(防止 外部通过new创建实例)
类内部声明变量
向外暴露一个静态的公共方法(getInstance),当调用该方法时,才去创建实例。
代码实现

/**
 * @Description: Singleton03单例模式-懒汉式(线程不安全)
 * @Author: atong
 * @Date: 2020-06-13
 * @Version:v1.0
 */
public class Singleton03 {
    public static void main(String[] args) {
        //测试
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);
        System.out.println(singleton.hashCode());
        System.out.println(singleton1.hashCode());

    }
}
class Singleton {
    //私有构造函数,方式外部通过new创建对象
    private Singleton () {
    }

    //类的内部声明变量
    private static Singleton singleton;

    //对外暴露一个静态方法,当调用该方法时,才去创建实例(singleton)
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

}

优点
能起到Lazy Loading的效果,但只能在单线程下使用。
缺点
如果在多线程下,一个线程进入了if(singleton ==null)判断语句块,还没来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
结论
在实际开发中,禁止使用这种方式。

懒汉式(线程安全)

关键点
构造器私有化(防止 外部通过new创建实例)
类内部声明变量
向外暴露一个静态的公共方法(getInstance),当调用该方法时,才去创建实例。且添加synchronized修饰,防止线程安全。
代码实现

/**
 * @Description: Singleton04单例模式-懒汉式(线程安全)
 * @Author: atong
 * @Date: 2020-06-15
 * @Version:v1.0
 */
public class Singleton04 {
    public static void main(String[] args) {
        //测试
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);
        System.out.println(singleton.hashCode());
        System.out.println(singleton1.hashCode());

    }
}
class Singleton {
    //私有构造函数,方式外部通过new创建对象
    private Singleton () {
    }

    //类的内部声明变量
    private static Singleton singleton;

    //对外暴露一个静态方法,当调用该方法时,才去创建实例(singleton)
    //添加synchronized修饰符,解决线程安全问题
    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

}

优点
解决了线程不安全问题
缺点
效率低
总结
在实际开发中,不推荐使用。

双重检查

关键点
构造器私有化(防止 外部通过new创建实例)
类内部声明变量,并使用volatile修饰(禁止指令重排)
先判空–>同步代码块–>再判空–>为null则创建对象(双重检查)
代码实现

/**
 * @Description: Singleton05单例模式-双重检查(Double-Check)
 * @Author: atong
 * @Date: 2020-06-15
 * @Version:v1.0
 */
public class Singleton05 {
    public static void main(String[] args) {
        //测试
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);
        System.out.println(singleton.hashCode());
        System.out.println(singleton1.hashCode());

    }
}
class Singleton {
    //私有构造函数,方式外部通过new创建对象
    private Singleton () {
    }

    //类的内部声明变量
    //volatile防止指令重排
    private static volatile Singleton singleton;

    //对外暴露一个静态方法,当调用该方法时,才去创建实例(singleton)
    //加入双重检查,解决线程安全问题,同时支持Lazy Loading,同时保证了效率
    //推荐使用
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

优点
线程安全;Lazy Loading;效率高
总结
推荐使用

静态内部类实现

关键点
外部类进行类装载的时候,静态内部类不会跟着进行类装载
当调用getInstance方法时,会使静态内部类加载,且只会装载一次,在装载的时候线程是安全的,既可以保证线程安全也可以起到Lazy Loading作用。
代码实现

/**
 * @Description: Singleton06单例模式-静态内部类
 * @Author: atong
 * @Date: 2020-06-15
 * @Version:v1.0
 */
public class Singleton06 {
    public static void main(String[] args) {
        //测试
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);
        System.out.println(singleton.hashCode());
        System.out.println(singleton1.hashCode());

    }
}
class Singleton {
    //私有构造函数,方式外部通过new创建对象
    private Singleton () {
    }

    //静态内部类,该内部类含有一个静态属性Singleton
    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    //对外暴露一个静态方法,直接返回SingletonInstance.INSTANCE
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }

}

优点
采用类装载机制保证初始化实例时只有一个线程
类的静态属性只会第一次加载类的时候初始化,所以这里,JVM帮助我们保证了线程安全性,在类的初始化时,别的线程是无法进入的。
总结
线程安全;Lazy Loading;效率高;推荐使用

枚举

代码实现

/**
 * @Description: Singleton07单例模式-枚举
 * @Author: atong
 * @Date: 2020-06-15
 * @Version:v1.0
 */
public class Singleton07 {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        Singleton singleton1 = Singleton.INSTANCE;
        System.out.println(singleton == singleton1);
        System.out.println(singleton.hashCode());
        System.out.println(singleton1.hashCode());
        singleton.sayOk();
    }
}

enum Singleton {
    INSTANCE;
    public void sayOk() {
        System.out.println("ook");
    }
}

优点
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,还能防止防止反序列化重新创建新的对象。

单例模式JDK源码分析

JDK中的Runtime类中用到了单例模式(饿汉式),如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值