[设计模式] 关于单例模式,单例模式的多种实现

什么是单例

简单解释 在当前内存(jvm)中某个类信息只能有一个实例存在。
原理 提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意事项

单例类只能有一个实例。
单例类必须自己创建自己的唯一实例。
单例类必须给所有其他对象提供这一实例。

简单解释 单例类的构造方法必须是私有的,只能在自己内部创建。(这样做是从根本上杜绝出现多个实例)
构造方法既然是私有的,那肯定只能在内部调用。
声明一个静态变量只给这个静态变量初始化,提供给外部调用。

优点

在内存里单个类信息只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例,还有高并发环境下频繁获取相同的对象或者属性(比如当前时间)

使用场景

系统内部创建多个对象会占用相应的资源,当前对象实例中的属性不需要或者不会变更的可以使用。
比如 操作系统中的回收站,没人会选择在操作系统中安装俩个回收站
java程序而言的话比如 学生表中有一系列的类型(性别,考试等级)这些值基本上是固定的,这些值可以通过后台返回给前台,通常会用到字典表,每次返回的对象属性信息都是一样的,就可以在项目启动初始化的时候把这些值加载到内存中,以后每次需要直接在内存中取出使用。避免重复创建对象,访问量很大的话内存占用也会少些。

代码实践

饿汉式单例

//饿汉式单例
public class Signlegton2 {

    private Signlegton2() {
        System.out.println("我来初始化了");
    }

    private static Signlegton2 signlegton = new Signlegton2();

    public static Signlegton2 getSignlegton(){
        return signlegton;
    }

    public static void main(String[] args) {
        Signlegton2 signlegton1 = Signlegton2. getSignlegton();
        Signlegton2 signlegton2 = Signlegton2. getSignlegton();
    }
}
//代码执行结果
我来初始化了

饿汉式加载,顾名思义不管你需不需要我都给你加载出来,这种加载方式天生是线程安全的,在类被引用的时候即创建一个实例,不需要考虑多个线程同时调用带来的问题。一般情况下单例模式的初始化会选择这个方式,但是也会有一个问题,如果这个实例我们并不需要,但是此类中的其他信息会被引用,而自动加载当前实例(因为是静态的),导致资源浪费,占用内存。最终我们可以考虑静态内部类的调用方式,可以防止这种情况。我们继续往下面看

懒汉式单例

//实现方式  懒汉式单例
public class Signlegton {

    private Signlegton() {
        System.out.println("我来初始化了");
    }

    private static Signlegton signlegton;

    public static Signlegton getSignlegton(){
        if (signlegton == null){
            signlegton = new Signlegton();
        }
        return signlegton;
    }

    public static void main(String[] args) {
        Signlegton  signlegton1 = Signlegton. getSignlegton();
        Signlegton  signlegton2 = Signlegton. getSignlegton();
    }
}


//代码执行结果
我来初始化了

顾名思义,懒汉式单例,也就是是说我不会自己实例化,你过来第一次调用才会初始化实例。这样调好处是如果当前对象没有地方使用,内存会先不占用。但是高并发调场景下就不是那样了,多个线程同时调用的情境下初始化可能会被同时间调用多次,并不能保证就是单例。

双重锁机制

//懒汉式单例  多个线程初始化时解决方案  双重锁机制
public class Signlegton1 {

    private Signlegton1() {
        System.out.println("我来初始化了");
    }

    private static Signlegton1 signlegton;

    public static Signlegton1 getSignlegton(){
        if (signlegton == null){
            synchronized (Signlegton1.class){
                if (signlegton == null){
                    signlegton = new Signlegton1();
                }
            }
        }
        return signlegton;
    }

    public static void main(String[] args) {
        Signlegton1 signlegton1 = Signlegton1. getSignlegton();
        Signlegton1 signlegton2 = Signlegton1. getSignlegton();
    }
}

//代码执行结果
我来初始化了

加锁,保证当前实例在创建时只能被一个线程使用,这样也就保证了任何环境下都只能有一个实例被创建在内存中。不过还是有问题,初始化的时候因为一些代码逻辑导致线程堵塞,后面多个线程等待,本身单例模式是为了提升效率的这样一整反而效率下降了。

静态内部类的方式

//静态内部类的方式
public class Signlegton3 {

    private Signlegton3() {
        System.out.println("我来初始化了");
    }

    public static class InitSignlegton3{
        public static final Signlegton3 signlegton = new Signlegton3();
    }
    public static void main(String[] args) {
        Signlegton3 signlegton1 = InitSignlegton3.signlegton;
        Signlegton3 signlegton2 = InitSignlegton3.signlegton;
    }
}

//代码执行结果
我来初始化了

推荐使用此方法用来创建单例模式,在引用当前对象的时候并不会直接创建无需使用的对象实例,还能保证线程安全。当然软件肯定是有bug出现的,这样可能还是有问题,万一初始化失败了呢。一定要抱着敬畏之心去看待代码,烦躁的时候要静下心来先去做一些其他的事情。

希望本文可以帮到你。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值