Java设计模式-单例模式

简介: Java设计模式-单例模式

单例模式原理

什么是单例对象?

有些对象我们只需要一个如线程池、缓存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为静态变量分配一次内存空间。

===> Java之static静态关键字详解

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

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) {}判断里面的,只有符合条件才会进入同步方法,减少了性能消耗。

补充

枚举实现单例原理



目录
相关文章
|
6月前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
866 157
|
6月前
|
设计模式 Java Spring
Java 设计模式之责任链模式:优雅处理请求的艺术
责任链模式通过构建处理者链,使请求沿链传递直至被处理,实现发送者与接收者的解耦。适用于审批流程、日志处理等多级处理场景,提升系统灵活性与可扩展性。
678 2
|
8月前
|
设计模式 缓存 Java
Java设计模式(二):观察者模式与装饰器模式
本文深入讲解观察者模式与装饰器模式的核心概念及实现方式,涵盖从基础理论到实战应用的全面内容。观察者模式实现对象间松耦合通信,适用于事件通知机制;装饰器模式通过组合方式动态扩展对象功能,避免子类爆炸。文章通过Java示例展示两者在GUI、IO流、Web中间件等场景的应用,并提供常见陷阱与面试高频问题解析,助你写出灵活、可维护的代码。
|
6月前
|
设计模式 算法 搜索推荐
Java 设计模式之策略模式:灵活切换算法的艺术
策略模式通过封装不同算法并实现灵活切换,将算法与使用解耦。以支付为例,微信、支付宝等支付方式作为独立策略,购物车根据选择调用对应支付逻辑,提升代码可维护性与扩展性,避免冗长条件判断,符合开闭原则。
1514 35
|
11月前
|
设计模式 缓存 安全
【设计模式】【创建型模式】单例模式(Singleton)
一、入门 什么是单例模式? 单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要全局唯一对象的场景,如配置管理、连接池等。 为什么要单例模式? 节省资源 场景:某些对象创
393 15
|
6月前
|
设计模式 消息中间件 传感器
Java 设计模式之观察者模式:构建松耦合的事件响应系统
观察者模式是Java中常用的行为型设计模式,用于构建松耦合的事件响应系统。当一个对象状态改变时,所有依赖它的观察者将自动收到通知并更新。该模式通过抽象耦合实现发布-订阅机制,广泛应用于GUI事件处理、消息通知、数据监控等场景,具有良好的可扩展性和维护性。
516 8
|
11月前
|
设计模式 缓存 安全
【高薪程序员必看】万字长文拆解Java并发编程!(8):设计模式-享元模式设计指南
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
228 0
|
8月前
|
设计模式 安全 Java
Java设计模式(一):单例模式与工厂模式
本文详解单例模式与工厂模式的核心实现及应用,涵盖饿汉式、懒汉式、双重检查锁、工厂方法、抽象工厂等设计模式,并结合数据库连接池与支付系统实战案例,助你掌握设计模式精髓,提升代码专业性与可维护性。
|
8月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
设计模式 Java 数据安全/隐私保护
Java 设计模式:装饰者模式(Decorator Pattern)
装饰者模式属于结构型设计模式,允许通过动态包装对象的方式为对象添加新功能,提供比继承更灵活的扩展方式。该模式通过组合替代继承,遵循开闭原则(对扩展开放,对修改关闭)。

热门文章

最新文章

下一篇
开通oss服务