设计模式:单例模式(懒汉模式/饿汉模式)

后续我会陆陆续续更新设计模式的一些介绍、应用、以及原理的文档。大家如果觉得对自己有用就点个关注吧。

引言

本文中的例子我尽量写的简单,避免一些我平时查资料时一些例子中出现的大量无用代码,产生让人阅读不下去的感觉

单例模式

 来!我们先讨论个问题,我先不和你们扯什么在Spring当中是否使用了单例模式,我就先问一下你们能理解"单" “例” “模” “式"这4个字吗?
 其实不难理解,我看网上有人说"在容器当中一个对象只有一个实例,这种设计思想叫单例模式”,这么说对,但是不准确,这么说不就又把Spring的IoC容器整进去了吗,其实应该是"在整个系统当中一个对象只存在一个对象实例,这种设计模式叫单例模式".

优缺点

优点:

  1. 就一个对象实例,节省系统资源,减轻gc(垃圾回收器)压力
  2. 可以实现一些资源共享

代码实现思路

 想实现一个单例模式,首先最大问题就是怎么才能让这个对象只被New一次,这个问题我们可以通过限制构造函数来达目的,将需要单例的对象,构造函数设置为私有的.这样以来只有我自己才能创建我自己.

public class Singleton {
    private static Singleton singleton;

    private Singleton(){
        System.out.println("我是一个单利对象");
    }

    public static Singleton getSingleton(){
        if(singleton == null){
            singleton = new Singleton();
            return singleton;
        }
        return singleton;
    }
}

 看上面的代码,由于构造器是私有的,所以任何一个对象都不能直接new Singleton()来创建对象,想获取Singleton对象只有一种办法调用静态方法getSingleton()而这个方法的功能就是只在第一次被调用时new一个Singleton对象,之后无论谁再调用这个方法来获取Singleton对象,都只会把之前的new号的对象返回.

知识点(创建类对象的加载顺序)

  1. 父类的静态代码块,静态变量的初始化
  2. 执行子类的静态代码块,静态变量的初始化
  3. 初始化父类实例变量
  4. 执行父类构造函数
  5. 初始化子类实例变量
  6. 执行子类构造函数

关于懒汉模式和饿汉模式

饿汉模式

 顾名思义:就是在程序加载的时候就创建了这个对象,无论这个对象以后会不会被用到,它都会被加载.当然这样做有好处也有坏处.
好处:不用考虑线程问题.因为他是线程安全的.
坏处:如果数量太多会拖慢程序启动速度,如果某些对象到程序结束都没有用到.内存就会被浪费

代码示例
public class Singleton {
    private final static Singleton singleton= new Singleton();

    private Singleton(){}

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

懒汉模式

 再次顾名思义:就是这个对象什么时候第一次用到它我才创建这个对象,否则一直不创建对象.
好处:会减少程序启动的负担,并且也会节省内容
坏处:这个写法相对比较费劲,因为你要确保他线程是安全的,不然就会出现两个请求同时访问这个方法,导致这个方法创建了2个以上的对象出来.(上面的那个例子就是懒汉模式,但是它线程不是安全的)

代码示例(不可以这么写)
先来一个线程不安全的示例
public class Singleton {
    private static Singleton singleton;

    private Singleton(){
        System.out.println("我是一个单利对象");
    }

    public static Singleton getSingleton(){
        if(singleton == null){
            singleton = new Singleton();
            return singleton;
        }
        return singleton;
    }
}

这样写当同时两个请求调用getSingleton()程序就会创建2个Singleton对象

再来一个线程安全的写法

大体思路就是用锁(synchronized),当一个线程调用这个方法的时候,如果其他线程也想调用这个方法,那么他就必须等当前线程调用完成,才可以在去调用方法

public class Singleton {
    private static Singleton singleton;

    private Singleton(){
        System.out.println("我是一个单利对象");
    }

    public static synchronized Singleton getSingleton(){
        if(singleton == null){
            singleton = new Singleton();
            return singleton;
        }
        return singleton;
    }
}

 这样写当然可以解决线程安全问题,但是这样就会造成每次调用这个方法时,这个方法都会被锁住,但其实我们只需要在方法第一次被调用,创建对象的时候锁住就行了.一旦单例对象被创建出来,这个方法就不需要在锁住了.所以我们优化一下

上个版本优化版
public class Singleton {
    private static Singleton singleton;

    private Singleton(){
        System.out.println("我是一个单利对象");
    }

    public static Singleton getSingleton(){
        if (singleton == null) {
            synchronized(Singleton.class) {
                //这里面必须在校验一遍,因为当有两个线程同时访问方法,
                //第一个线程先进去创建对象,如果不加这层校验,
                //第二个线程进去还是会创建一个对象
                if (singleton == null) {
                    singleton = new Singleton();
                    return singleton;
                }
            }
        }
        return singleton;
    }
}

 这里面为什么要多加一层校验?,因为当有两个线程同时访问方法,第一个线程先进去创建对象,如果不加这层校验,第二个线程进去还是会创建一个对象

再来一个高级版本
public class Singleton {

    private Singleton(){
        System.out.println("我是一个单利对象");
    }

    private static class SingletInside{
        private static Singleton singleton = new Singleton();
    }

    private static Singleton getSingleton(){
        return SingletInside.singleton;
    }

}

 这个方法说道就多了,看没看到类里面有一个静态内部类,之所以能这写是因为,在Singleton类被new出来以后,如果没有调用getSingleton()方法那么这个静态内部类就不会被初始化,当第一次调用getSingleton()方法时Singleton单例对象也随着SingletInside内部类的初始化而被创建出来.而之后再调用SingletInside这个静态内部类就不会在初始化了(类在程序中只会加载一次)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值