• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【设计模式与体系结构】创建型模式-单例模式

引言

张三和其舍友收假后回到宿舍,并闲聊了起来。张三说:“我昨天吃了家店,菜品很不错。”其舍友也说:“我昨天也在校门口一家新开的店吃了一下,那家新开的店也很不错。”张三说:“我昨天吃的是鸡公煲,你吃的是什么?”其舍友说:“巧了,我吃的也是鸡公煲。”张三说:“学校门口有好几家鸡公煲呢,你吃的是哪一家的?”其舍友说:“就校门口正对面那家。”此时张三一拍大腿:“哦!说了半天,原来我们吃的是同一家店。”

有时候不同的人对同一个事物的描述是会有些许不同的,但是实际讲的其实是同一件事情。比如张三和其舍友吃鸡公煲的故事,不会因为两个人对店的描述不同,就导致他们吃的“同一家店”变成“不同的店”。张三去校门口正对面那家店吃鸡公煲,和其舍友去校门口正对面那家店吃鸡公煲,这两个人去的都会是同一家店。那么这个店呢,就是个唯一确定的店,无论是他们谁去吃,去的都是同一家店,而不会因为描述者的不同而导致店不同。

类似于“去同一家店吃鸡公煲”的事情,在计算机领域有一种设计模式叫做单例模式,指的就是整个程序无论在何处访问,访问到的都是同一个实例。鸡公煲店就相当于单例的实例,张三和其舍友就相当于不同的类,这两个类访问同一个单例,虽然功能不同(相当于二人对店的描述不同),但是访问的仍然是同一个单例(说的仍然是同一家店)。

简介

单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,这个类提供了一种访问其唯一对象的方法,可以直接访问,不需要实例化该对象。

单例模式的角色

  1. 单例类:创建一个实例的类
  2. 访问类:访问单例类

单例模式的类型

  1. 饿汉式:类加载就会导致该单例对象被创建
  2. 懒汉式:类加载不会导致该单例对象被创建,而是首次使用时才创建

单例模式的特点

  1. 唯一性:单例类在整个应用程序只会存在一个实例
  2. 全局访问:通过一个静态方法或属性,可以在整个程序的任何地方方便地获取到单例的实例

正文

饿汉式-静态变量方式

以张三与其舍友吃鸡公煲的案例,写一个饿汉式-静态变量方式的程序(毕竟这两人一见面就聊吃的,多半都是饿汉)。

  1. 创建一个实体类People.java
public class People {
    String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void order() {//点菜方法
        System.out.println(getName() + "点了一份鸡公煲。");
        RestaurantSingleton restaurantSingleton = RestaurantSingleton.getInstance();
        System.out.println(getName() + "去的饭店是" + restaurantSingleton.hashCode());
    }
}
  1. 创建一个饭店单例类RestaurantSingleton.java
public class RestaurantSingleton {
    //私有构造方法
    private RestaurantSingleton() { }
    //在本类中创建本类对象
    private final static RestaurantSingleton restaurantSingleton = new RestaurantSingleton();
    //提供一个公共的访问方法,供外界访问
    public static RestaurantSingleton getInstance() {
        return restaurantSingleton;
    }
}
  1. 创建两个用户访问类
People zhangsan = new People();
zhangsan.setName("张三");
zhangsan.order();
People lisi = new People();
lisi.setName("李四");
lisi.order();
  1. 运行结果如下

    说明虽然饭店单例类RestaurantSingleton.java的调用者不同,但是获取到的单例实例是同一个。

饿汉式-静态代码块方式

public class RestaurantSingleton {
    //私有构造方法
    private RestaurantSingleton() { }
    //声明 RestaurantSingleton 类型的变量
    private final static RestaurantSingleton restaurantSingleton;
    //在静态代码块中初始化 restaurantSingleton 变量
    static {
        restaurantSingleton = new RestaurantSingleton();
    }
    //提供一个公共的访问方法,供外界访问
    public static RestaurantSingleton getInstance() {
        return restaurantSingleton;
    }
}

懒汉式-线程不安全方式

想开鸡公煲店的老板,当然是想把店开在有顾客的地方,如果开个完全没有顾客的店,那显然他就做了场赔本生意。
对于单例模式饿汉式,由于类的实例是静态的,因此会一直存在于内存中。若长时间不使用,会造成内存的浪费。那么将单例的实例化延迟到被使用时,会更节约系统资源,于是引入了懒汉式单例模式:

public class RestaurantSingleton {
    //私有构造方法
    private RestaurantSingleton() { }
    //声明 RestaurantSingleton 类型的变量
    private static RestaurantSingleton restaurantSingleton = null;//仅声明,未赋值
    //提供一个公共的访问方法,供外界访问
    public static RestaurantSingleton getInstance() {
        if (restaurantSingleton == null) {//若未赋值过则进行赋值,否则直接返回
            restaurantSingleton = new RestaurantSingleton();
        }
        return restaurantSingleton;
    }
}

上述代码不会在类加载时就创建一个静态变量,而是在系统真正调用这个变量时才进行创建,会使得使用的效率更高。但是同时也引入了新的问题:
有两个老板,在相同的时间段发现了同一块空地,这两个老板都想在这块空地开一家鸡公煲店,于是各自回去准备建造设施。后面当其中一个老板准备好家伙要来盖店的时候,却发现早已被另一个老板抢占先机盖了店。于是二者争执不下,都说自己先发现的,自己来的时候还没有被开发。这说的就是线程不安全。

懒汉式-线程安全方式

于是一个老板想到了一个办法,要去探索一块地之前,先把方圆十里全用铁栅栏围起来,然后自己再安心地去看看有没有空地,有空地再建起来。相当于对于一个程序,可以使用关键字 synchronized 修饰函数,使得不同对象对同一个函数的访问是串行的。

public class RestaurantSingleton {
    //私有构造方法
    private RestaurantSingleton() { }
    //声明 RestaurantSingleton 类型的变量
    private static RestaurantSingleton restaurantSingleton = null;//仅声明,未赋值
    //提供一个公共的访问方法,供外界访问
    //synchronized 关键字可以使得对该函数的访问是串行的
    public static synchronized RestaurantSingleton getInstance() {
        if (restaurantSingleton == null) {//若未赋值过则进行赋值,否则直接返回
            restaurantSingleton = new RestaurantSingleton();
        }
        return restaurantSingleton;
    }
}

懒汉式-双重检查锁方式

有个老板每次把方圆十里的地都围起来了,但是每次去发现空地的时候,都发现那些空地早就被抢占先机了。于是老板就很苦恼,每次围地都很费劲,但是最后却发现没有自己可以建鸡公煲店的地方。于是他想了一个法子:先去找到空地,如果有空地再去围方圆十里,围完以后再去检查一下空地是不是还是空着的,如果是就可以安心的建鸡公煲店了。这就相当于程序中的双重检查锁方式。

public class RestaurantSingleton {
    //私有构造方法
    private RestaurantSingleton() { }
    //声明 RestaurantSingleton 类型的变量
    private static RestaurantSingleton restaurantSingleton = null;//仅声明,未赋值
    //提供一个公共的访问方法,供外界访问
    public static RestaurantSingleton getInstance() {
        //第一次判断,若 restaurantSingleton 不为 null,则不进入抢锁阶段
        if (restaurantSingleton == null) {
            //进行抢锁操作
            synchronized (RestaurantSingleton.class) {
                //抢到锁后,再次判断 restaurantSingleton 是否为 null,若是则赋值,否则直接返回
                if (restaurantSingleton == null) {
                    restaurantSingleton = new RestaurantSingleton();
                }
            }
        }
        return restaurantSingleton;
    }
}

posted on 2024-12-13 23:12  RomanLin  阅读(25)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3