IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java设计模式之单例模式【创建型】 -> 正文阅读

[Java知识库]Java设计模式之单例模式【创建型】

目录

?编辑

前言

开篇

什么是单例模式?

单例模式能解决什么问题?

项目中哪些地方使用了单例模式?

如何实现单例模式?

第一版

第二版【1.0】

第二版【2.0】

第二版【3.0】

第二版【最终版】

第三版


前言

大家好,我是爷爷的茶七里香,最近突然想做一个设计模式的系列,大家都知道设计模式有23种,而设计模式是由前人大量实践总结出来的,它是解决一些问题的最佳解决方案,我们今天就讲下单例模式中相对简单的单例模式吧。

开篇

什么是单例模式?

单例模式是在程序运行过程中一个类只有一个实例对象,不管调用了多少次,调用的都会是这个对象。

单例模式能解决什么问题?

首先我们使用单例模式可以提高我们的系统性能,试想一下,某个可以复用的对象创建过程十分复杂,每使用一次就要进行创建销毁的过程,?那么就浪费了不必要的资源了,一个简单的例子就是用来和数据库链接的对象,创建这个对象是比较消耗资源的,像这种对象每进行一次链接就创建一次新的对象,即浪费了空间又浪费了时间。

项目中哪些地方使用了单例模式?

在项目中我们的controller层调用service层、service层调用dao层,这两种情况使用的就是单例模式,它们为什么使用的是单例模式呢?因为它们的成员变量是不会改变的,它是静态的,如果说它们中的有一个成员变量的值是动态改变的,那么这个时候就不能用单例模式了,如果这个时候使用单例模式那么就会出现变量被污染的情况,比如两个线程去做操作,因为两个线程操作的是同一个对象,假设这个对象的成员变量有一个变量a,线程1去把a变量值修改为1,然后线程2去把a变量值修改为2,那么这个线程1读到的a变量就不是1而是2了,只有使用多利模式,让它们操作的对象不是同一个就不会出现这种情况了。想一下,controller层调用service会出现变量动态改变的情况吗?显然不会吧!因而才用单例模式!

如何实现单例模式?

要实现单例模式要看以下几点:

  • 是否懒加载
  • 是否线程安全
  • 是否能被反射破坏(这种情况基本上是人为的,不需要考虑)

第一版

public class OneObject {
    // 私有构造器
    private OneObject() {}
    private static OneObject oneObject = new OneObject();
    public static OneObject getOneObject() {
        return oneObject;
    }
}

测试:

public static void main(String[] args) {
        System.out.println("OneObject1 = " + OneObject.getOneObject());
        System.out.println("OneObject2 = " + OneObject.getOneObject());
        System.out.println("OneObject3 = " + OneObject.getOneObject());
    }

打印结果:

OneObject1 = xyz.keydoisdls.test.OneObject@312b1dae
OneObject2 = xyz.keydoisdls.test.OneObject@312b1dae
OneObject3 = xyz.keydoisdls.test.OneObject@312b1dae?

这种写法可以避免了性能的浪费也能做到线程安全,但是它并不是懒加载的,因为它是在项目启动就把对象创建好了,如果这个对象一直都用不到,那么就浪费内存了,这种写法用不用就看个人情况吧!?

注:懒加载的意思是当我需要使用这个对象的时候才去创建,并不会在项目启动的时候就把对象创建好了!?项目启动就把对象创建好了,可能会一辈子都用不到这个对象,那么就浪费资源了,所以使用懒加载可以减少资源的占用。

第二版【1.0】

public class OneObject {
    // 私有构造器
    private OneObject() {}
    // 懒加载
    private static OneObject oneObject = null;
    public static OneObject getOneObject() {
        if (oneObject == null){
            // 第一次进来是空的,创建一个实例
            oneObject = new OneObject();
        }
        // 不为空直接返回
        return oneObject;
    }
}

这种写法可以做到懒加载,但是如果是多线程的情况下会发生什么呢?可能会出现两条线程同时进入进行null值判断,这样就出现了一个类有两个实例了,这样它就不是单例了,所以我们需要考虑是否安全的情况,因此这种写法不采用,会出现线程不安全的情况。?

第二版【2.0】

public class OneObject {
    // 私有构造器
    private OneObject() {}
    private static OneObject oneObject = null;
    // 相比第一种多了synchronized关键字(自动锁)
    public static synchronized OneObject getOneObject() {
        if (oneObject == null){
            // 第一次进来是空的,创建一个实例
            oneObject = new OneObject();
        }
        // 不为空直接返回
        return oneObject;
    }
}

可以看到在函数上多了一个synchronized关键字,这种写法可以做到懒加载+线程安全,但是比较消耗性能,你想一下,我们什么时候需要加锁呢?只有在创建对象的时候加,对吧?但是现在我连获取一个对象都要线程去竞争,那么就很浪费性能了,所以这种写法也不可取!?

第二版【3.0】

public class OneObject {
    // 私有构造器
    private OneObject() {}
    // volatile关键字避免指令重排导致空指针
    private volatile static OneObject oneObject = null;
    public static OneObject getOneObject() {
        if (oneObject == null){
            // 对第一次进来创建对象加锁
            synchronized (OneObject.class){
                oneObject = new OneObject();
            }
        }
        // 不为空直接返回
        return oneObject;
    }
}

?该写法相较于上一个版本避免了性能的问题,只有在创建对象的时候上锁,获取对象不会进行上锁,但是这个写法还是会有问题,如果两条线程同时进行判空操作,两条线程不管是哪条先抢到锁执行了new OneObject()的操作,另外一条也肯定会执行该操作,所以也会出现线程不安全的情况。

第二版【最终版】

public class OneObject {
    // 私有构造器
    private OneObject() {}
    // volatile关键字避免指令重排导致空指针
    private volatile static OneObject oneObject = null;
    public static OneObject getOneObject() {
        if (oneObject == null){
            // 对第一次进来创建对象加锁
            synchronized (OneObject.class){
                // 再次判断
                if (oneObject == null){
                    oneObject = new OneObject();
                }
            }
        }
        // 不为空直接返回
        return oneObject;
    }
}

这种写法可以完美的解决了懒加载+线程安全,看代码可以看到我们进行了两次判断,这个也叫做双检锁,让我们假设两条线程同时通过了第一个判断,第一条线程恰好抢到锁了,执行代码块的内容,又进行了一次判断,这个时候肯定是空的,创建好了对象并赋值释放锁;轮到第二条线程进入代码块了,当它又进行一次判断时发现不为空了,它就不做后续的操作了,然后释放锁,返回对象;

第三版

public class OneObject {
    // 私有构造器
    private OneObject() {
    }

    // 静态内部类
    private static class One {
        private static final OneObject ONE_OBJECT = new OneObject();
    }

    public static  OneObject getOneObject(){
        return One.ONE_OBJECT;
    }
}

我们都知道静态内部类是在调用的时候才会去初始化,所以我们可以利用这个特性实现单例的创建,既能做到线程安全也能做到懒加载,更是简化了代码;

以上能做到单例模式,但是都会被反射破坏,反射可以忽略任何,它可以直接调用构造器,即使你的构造器是私有的,但是这点可以不需要考虑,反射基本是人为的,只要你不去恶意的搞事情,基本忽略,如果硬要做到的话可以自己去了解下枚举类,使用枚举类实现单例的话可以做到不能被反射破坏,也能做到线程安全,但是它不是懒加载的,感兴趣的话自己私下了解下吧!

今天就到这里啦~对你有帮助的话不妨留个赞呗!

?🥇原创不易,还希望各位大佬支持一下!

👍点赞,你的认可是我创作的动力?!

🌟收藏,你的青睐是我努力的方向!

??评论,你的意见是我进步的财富!?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-14 22:21:09  更:2022-06-14 22:27:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 18:38:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码