设计模式(四)-单例模式

作者: 若丨寒 | 来源:发表于2019-03-29 14:17 被阅读2次

单例模式原理

什么是单例对象?

有些对象我们只需要一个如线程池、缓存dataSource、硬件设备等。如果有多个实例会造成相互冲突、结果不一致的问题,毕竟你有我也有,但是你有的和我有的不一定真的一模一样,是同一个。使用单例模式可以确保一个类最多只有一个实例,并提供一个全局的访问点。

public class Test {
    public class ABC {
        public ABC() {
        }

//        private ABC() {  //为这个内部类申明私有的构造方法,外部则无法通过new初始化,只能类自己初始化
//        }
//        ABC n1 = new ABC();
    }

    public class CDB {
        public CDB() {
            ABC n1, n2;
            n1 = new ABC();
            n2 = new ABC();
            System.out.println("CBD: " + (n1 == n2));    //false
        }
    }

    public static void main(String[] args) {
        ABC n1, n2;
        n1 = new Test().new ABC();
        n2 = new Test().new ABC();
        System.out.println("main: " + (n1 == n2));   //false
        new Test().new CDB();
    }
}

那么有什么方法可以使得每次new出来的对象都是同一个呢,看看下面单例模式类图,就可以找到一些思路了!

Singleton(单例)
static uniqueInstance(静态的唯一对象申明)
private singleton() (私有的实例化方法)
static getInstance() (全局访问点)

编码实战

了解了上面的内容,我们来写一个简单的单例模式代码,代码如下:

public class Singleton {
    private static Singleton uniqeInstance = null;    //静态变量

    private Singleton() {    // 私有的构造方法,外部无法使用
    }

    public static Singleton getInstance() {
        if (uniqeInstance == null) {
            uniqeInstance = new Singleton();
        }
        return uniqeInstance;
    }
}

静态变量由于不属于任何实例对象,是属于类的,所以在内存中只会有一份,在类的加载过程中,JVM为静态变量分配一次内存空间。

这个场景我们想象一下:一个食品工厂,工厂只有一个,然后工厂里也只有一个锅,制作完一批食品才能制作下一批,这个时候我们的食品工厂对象就是单例的了,下面就是模拟实现的代码,代码的单例实现和上面的简单实现不同,做了优化处理,稍后会解释为什么要优化

public class ChocolateFactory {
    private boolean empty;   // 空锅
    private boolean boiled;  // 加热
    public volatile static ChocolateFactory uniqueInstance = null;

    private ChocolateFactory() {
        empty = true;    // 锅是空的
        boiled = false;  // 还没加热
    }

    public static ChocolateFactory getInstance() {
        if (uniqueInstance == null) {
            synchronized (ChocolateFactory.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new ChocolateFactory();
                }
            }
        }
        return uniqueInstance;
    }
    // 第一步装填
    public void fill() {
        if (empty) {  // 锅是空的
            // 添加原料巧克力动作
            empty = false;  // 锅装满了,不是空的
            boiled = false;  // 还没加热
        }
    }
    // 第三步倒出
    public void drain() {
        if ((!empty) && boiled) {  // 锅不是空的,已经加热
            // 排出巧克力动作
            empty = true;   //出锅,锅空了
        }
    }
    // 第二步加热
    public void boil() {
        if ((!empty) && (!boiled)) {  // 锅不是空的,没加热
            // 煮沸
            boiled = true;  // 已经加热
        }
    }
}

单例模式的问题及优化

问题

在多线程的情况下,会有时间片的概念,cpu竞争,这刚好就是单例模式可能会发生问题的时候,会发生什么样的问题呢?以食品加工厂代码为例

public synchronized static ChocolateFactory getInstance() {
       if (uniqueInstance == null) {
           uniqueInstance = new ChocolateFactory();
       }
       return uniqueInstance;
   }

在多线程情况下会实例化出两个对象

优化解决

同步(synchronized)getInstance方法

线程1执行到if (uniqueInstance == null),被线程2抢走了执行权,此时线程1还没有new对象;线程2同样来到if (uniqueInstance == null),发现没有对象实例,也打算实例化对象;最后线程1线程2都会执行uniqueInstance = new ChocolateFactory();此时可以在getInstance()方法前加上synchronized修饰符同步方法,但是在多线程调用比较频繁的时候,这种方式比较耗费性能。

“急切”创建实例

public class ChocolateFactory {
    public static ChocolateFactory uniqueInstance = new ChocolateFactory();  //“急切”创建实例
    public static ChocolateFactory getInstance() {
       if (uniqueInstance == null) {
           uniqueInstance = new ChocolateFactory();
       }
       return uniqueInstance;
   }
}

public static ChocolateFactory uniqueInstance = new ChocolateFactory();在应用启动的时候就加载初始化一次实例对象,这个时候多线程调用永远也只会有一个实例,因为if (uniqueInstance == null)的结果一直是false;但如果这对单例对象在应用中没有地方用到,使用这种方式则耗费掉了一些内存空间

双重检查加锁(最佳)

public class ChocolateFactory {
    //用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。
    public volatile static ChocolateFactory uniqueInstance = null;   
    public static ChocolateFactory getInstance() {
        if (uniqueInstance == null) {
            synchronized (ChocolateFactory.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new ChocolateFactory();
                }
            }
        }
        return uniqueInstance;
    }
}

首先public volatile static ChocolateFactory uniqueInstance = null;没有在应用启动的时候就初始化对象,节省了内存;其次synchronized修饰的代码块是再if (uniqueInstance == null) {}判断里面的,只有符合条件才会进入同步方法,减少了性能消耗。

最后

大家觉得不错可以点个赞在关注下,以后还会分享更多文章!同时我的的专栏:Java架构技术栈,以后还会分享更多文章!

相关文章

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • 单例模式

    面向对象设计的设计模式(四):单例模式

  • Java四种单例设计模式

    Java中的四种单例模式 单例模式是最容易理解的设计模式之一,介绍Java中单例模式的四种写法。 1.基本单例模式...

  • python中OOP的单例

    目录 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例

    目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 设计模式 - 单例模式

    设计模式 - 单例模式 什么是单例模式 单例模式属于创建型模式,是设计模式中比较简单的模式。在单例模式中,单一的类...

  • 设计模式一、单例模式

    系列传送门设计模式一、单例模式设计模式二、简单工厂模式设计模式三、工厂模式设计模式四、抽象工厂模式 简单单例(推荐...

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • 阿里架构师工作十年接触过Java框架设计模式(续上文)

    1-25续 四、单例设计模式(Singleton Pattern) 4.1 介绍 单例模式是一种创建型模式,单例模...

网友评论

    本文标题:设计模式(四)-单例模式

    本文链接:https://www.haomeiwen.com/subject/stxkbqtx.html