首页 > 其他分享 >单例设计模式

单例设计模式

时间:2023-09-04 22:55:41浏览次数:30  
标签:对象 private Person static 单例 线程 设计模式

单例设计模式

基本介绍

类的单例设计模式,就是采取一定的方式保证在整个的软件系统中,对某个类只能有一个对象实例存在,且类提供一个静态方法,用以获取该对象。
例如Hibernate的SessionFactory,它是sql会话工厂,这个对象一定是很重的(创建需要加载很多资源和时间),一般情况下,只需要一个SessionFactory就够了。

单例模式的八种实现方式

1.饿汉式(静态常量) 2.饿汉式(静态代码块) 3.懒汉式(线程不安全) 4.饿汉式(线程安全,同步方法) 5.饿汉式(线程不安全,同步代码块) 6.双重检查 7.静态内部类 8.枚举类
字体加粗的推荐使用。

饿汉式(静态常量)

实现步骤
1.构造器私有化(杜绝new) 2.在类的内部创建实例 3.在类的内部创建一个静态方法,返回创建的对象。
代码实现

class Person{
	//1.通过静态属性,创建对象
    private final static Person p = new Person();
	//2.构造器私有化
    private Person(){

    }
	//3.提供public 的静态方法,用以返回单例对象
    public static Person getInstance(){
        return p;
    }
}

饿汉式(静态代码块)

代码实现

class Person{

    private final static Person p;
	//1.通过静态代码块,创建单例对象
    static{
        p = new Person();
    }
	//2.构造器私有化
    private Person(){

    }
	//3.提供public 的静态方法,用以返回单例对象
    public static Person getInstance(){
        return p;
    }
}

静态代码块和静态常量,一样效果,只是使用静态代码块,完成单例对象的创建。

饿汉式分析

优点 : 写法简单,在完成类的装载时,完成单例对象的创建,避免了线程同步问题
缺点:在类装载,就完成实例化,没有达到懒加载的效果(懒加载,就是使用时,才创建)。如果项目启动到结束,都没有使用到该单例对象,会造成内存的浪费。
这种方式基于class loader机制避免了多线程的同步问题,不过单例对象在类装载时,就完成了对象的创建。在单例模式中大多数情况下,虽然都是调用getInsatnce()方法,完成对象创建,但是
但是导致类加载的原因有多种,比如其他的静态方法被使用,导致的类加载,这时候就达不到懒加载的效果。
结论:这种单例模式可用,但是可能会造成内存浪费,且达不到懒加载效果。

懒汉式(线程不安全)

代码实现

class Person {
    private static Person p;

    private Person() {
    }
	
    public static Person getInstance() {
        //线程不安全
        if (p == null) {
            p = new Person();
        }
        return p;
    }
}

优缺点说明

  • 1.起到了懒加载的效果,但是只能在单线程模式下使用。
  • 2.如果在多线程下,一个线程进入了if(p == null)的判断,还没来得及往下执行,另一个线程也通过了这个判断语句,这边会产生多个实例。所以多线程环境下不要使用这种方式。
  • 3.结论,开发中,不要使用这种方式。

懒汉式(线程安全,同步方法)

代码实现

class Person {
    private Person() {
    }

    private static Person p;
	//利用synchronized关键字同步方法
    public static synchronized Person getInstance() {
        if (p == null) {
            p = new Person();
        }
        return p;
    }
}

优缺点说明

  • 1.解决了线程不安全的问题
  • 2.效率低,每个线程在获取单例对象时,执行getInstance方法都要进行同步,而其实这个方法只执行一次实例代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低。
  • 3.结论,在实际开发中,不推荐此方式。

懒汉式(线程不安全,同步代码块)

代码实现

class Person{
    private Person(){}

    private static Person p;

    public static Person getInstance(){
        if(p == null){
            synchronized(Person.class){
                p = new Person();
            }
        }
        return p;
    }
}

优缺点说明

  • 1.这种方式,本意是想对第四种方式的改进,因为前面的同步方法效率太低了,改为同步产生实例化的代码块
  • 2.但是这种方式,并不能起到线程安全的作用,和第三种实现方式一致,假如一个线程进行了if(p == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这样就会产生多个实例
  • 3.结论,实际开发,不推荐使用这种方式。

双重检查

代码实现

class Person{
    private Person(){}

    private static volatile Person p;//使用volatile关键字为了确保多线程环境下的可见性和有序性。

    public static Person getInstance(){
        if(p == null){
            synchronized (Person.class){
                if(p == null){
                    p = new Person();
                }
            }
        }
        return p;
    }
}

优缺点说明

  • 1.Double-Check概念,是多线程开发中常用的。如代码所示,进行两次if(p == null)的检查,就可以保证线程安全了。
  • 2.这样实例化代码只执行一次,后面再次访问,判断if(p == null)直接return实例化对象,也避免了反复进行方法同步。
  • 3.线程安全,延迟加载,效率高。
  • 4.结论,即实现了单例模式,也满足了懒加载,效率高,且线程安全,推荐使用。

volatile关键字

在多线程环境下,每个线程都有自己的工作内存,其中包含了被线程使用的变量的副本。当一个线程修改了变量的值时,这个修改可能不会立即被其他线程看到,而是在某个时刻才会被刷新到主内存中,这就导致了可见性问题。
使用 volatile 关键字修饰一个变量,可以确保变量的修改对其他线程是可见的。当一个线程修改了 volatile 变量的值,会立即将修改后的值刷新到主内存中,其他线程在读取该变量时会从主内存中获取最新的值。
此外,volatile 关键字还可以保证变量的有序性。在多线程环境下,指令重排序可能会导致代码执行顺序与预期不符,使用 volatile 关键字修饰的变量会禁止指令重排序,保证代码的执行顺序与程序中的顺序一致。需要注意的是,volatile 关键字只能保证对单次读/写的原子性,不能保证复合操作的原子性。如果需要保证复合操作的原子性,可以使用锁或者其他同步机制。
在单例模式下的双重检查的实现方式中,需要对单例对象(静态属性)做volatile修饰,因为java中对象的生成分为 分配内存、初始化、返回对象引用 但是由于指令的无序性。即是可能会出现 分配内存 返回对象引用 初始化的指令顺序。但是是1.5之前的jdk版本会有此问题,当然概率很小。在jdk1.5后可以使用volatile关键字保证创建一个对象的指令按顺序执行。

静态内部类

class Person{
    private Person(){}

    private static class SubPerson{
        private static final Person INSTANCE = new Person();
    }

    public static Person getInstance(){
        return SubPerson.INSTANCE;
    }   
}

优缺点说明

  • 1.这种方式采用了类装载机制保证初始化实例时只有一个线程。
  • 2.静态内部类在Person类被装载时,并不会立即实例化,而是在getInstance方法,被调用,才会状态SubPersonl类,从而完成Person的实例化。
  • 3.类的静态属性,只会在第一次加载类的时候初始化,所以这样的方式,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
  • 4.优点,避免了线程不安全,利用静态内部类特定实现延迟加载,效率高。
  • 5.结论 推荐使用。

枚举

代码实现

enum Person{
    INSTANCE;
}

优缺点说明

  • 1.这借助JDK1.5添加的枚举来实现单例模式,可以避免多线程同步问题,而且还能防止反序列化,重新创建新的对象。
  • 2.结论 推荐使用,但是此方法会隐性的继承Enum类,需要注意。

单例模式的应用

java.lang.Runtime类就是经典的单例模式

注意事项和细节

  • 1.单例模式保证了系统内部,一个类只存在一个对象,节省了系统资源,对于一些需要频繁创建和销毁的对象,使用单例模式可提高系统的性能。
  • 2.获取单例对象,不是new,而是调用对应的获取对象的静态方法。
  • 3.单例模式的使用场景,需要频繁的进行创建和销毁对象。创建对象耗时过多,或者耗费资源过多(即重量级对象),但又经常使用的对象,如频繁访问数据库,文件的对象(数据源,SessionFactory等)。
    只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。

标签:对象,private,Person,static,单例,线程,设计模式
From: https://www.cnblogs.com/changming06/p/17678240.html

相关文章

  • 设计模式- 单例模式
    单例模式是一种创建型设计模式,可以保证一个类型有且只有一个实例存在单例模式的适用于什么场景当一个类在程序运行期间只需要一个实例的时候,就可以考虑将其做成单例模式例如一些全局的配置,用来储存程序运行期间全局的共享配置,或者可以做一个简单的消息管道,程序中所有......
  • C#中单例模板
    泛型单例/***泛型单例模板where限制这个单例类必须要能被new出来*/publicclassSingleton<T>:IDisposablewhereT:new(){privatestaticTinstance;publicstaticTInstance{get{if(instance==null)instance=new......
  • 设计模式备忘录+命令模式实现Word撤销恢复操作
    文章目录前言思路代码实现uml类图总结前言最近学习设计模式行为型的模式,学到了备忘录模式提到这个模式可以记录一个对象的状态属性值,用于下次复用,于是便想到了我们在Windows系统上使用的撤销操作,于是便想着使用这个模式进行一次模仿复现思路以下是按照备忘录和命令模式结合的思路......
  • 设计模式行为模式-访问者模式
    文章目录介绍基本原理1.定义访问者模式2.角色及其职责2.1元素(Element)2.2具体元素(ConcreteElement)2.3访问者(Visitor)2.4具体访问者(ConcreteVisitor)2.5对象结构(ObjectStructure)3.访问者模式的工作流程适用场景1.对象结构稳定,但需要经常增加新的操作2.处理复杂的对象结构,并......
  • 设计模式-原型模式详解
    文章目录前言理论基础1.原型模式定义2.原型模式角色3.原型模式工作过程4.原型模式的优缺点实战应用1.原型模式适用场景2.原型模式实现步骤3.原型模式与单例模式的区别原型模式的变体1.带有原型管理器的原型模式2.懒汉式单例模式的原型模式实现3.细粒度原型模式总结前言......
  • 设计模式-装饰模式
    文章目录一、简介二、基本概念三、装饰模式的结构和实现类图解析:装饰器的实现方式继承实现:组合实现:继承和组合对比四、装饰模式的应用场景五、与其他模式的关系六、总结一、简介装饰模式是一种结构型设计模式,它允许动态地向对象添加额外的功能。二、基本概念装饰模式定义:在不改变......
  • 设计模式-迭代器
    文章目录1.引言1.1概述1.2设计模式1.3迭代器模式的应用场景1.4迭代器模式的作用2.基本概念2.1迭代器Iterator2.2聚合Aggregate2.3具体聚合ConcreteAggregate3.Java实现迭代器模式3.1Java集合框架3.2Java迭代器接口3.3Java迭代器模式实现示例4.迭代器模式的优......
  • Go 设计模式中策略模式
    鱼弦:全栈领域创作新星创作者、51CTO(Top红人+专家博主)、github开源爱好者(go-zero源码二次开发、游戏后端架构https://github.com/Peakchen)  在Go设计模式中,策略模式是一种行为型模式,用于在运行时选择算法的行为。策略模式将不同的算法封装成各自的策略对象,并使得这些策略对象......
  • Go 设计模式中访问者模式
    鱼弦:全栈领域创作新星创作者、51CTO(Top红人+专家博主)、github开源爱好者(go-zero源码二次开发、游戏后端架构https://github.com/Peakchen)在Go设计模式中,访问者模式(VisitorPattern)是一种行为型模式,用于将算法与数据结构分离,使得算法可以独立地操作数据结构的不同元素,同时遵循......
  • c++单例模式总结
    分类懒汉式:实例对象在第一次被使用时才进行初始化。饿汉式:实例在定义时就被初始化。特点1、构造函数和析构函数私有化,不允许外部创建实例对象。2、拷贝构造函数和复制运算符重载被delete,不允许产生新的实例。3、内部定义一个私有的静态数据成员,该成员为本类的实例化对象。4......