Java设计模式之单例模式

单例模式

  • 单例模式定义
  • 单例模式结构和实现
    • 单例模式结构
      • 静态变量与非静态变量
    • 单例模式的实现
  • 饿汉式单例
  • 懒汉式单例
  • 单例模式优缺点
  • 适用环境

单例模式定义

单例模式定义就是确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。属于设计模式的创建型模式
单例模式有3个要点:一是某个类智能有一个实例;二是它必须自行创建这个实例;三是它必须自行抽象整个系统提供这个实例

单例模式结构和实现

单例模式结构

只包含一个类,即单例类。
对于Singleton,在类内创建唯一实例,通过静态方法GetInstance()让客户端可以使用它唯一实例;且为了防止被外部对单例类实例化,将单例类的构造函数访问权限设为private;在单例类内定义一个Singleton类型的静态对象供外部共享访问的唯一实例。

Java设计模式之单例模式_第1张图片

静态变量与非静态变量

被static关键字修饰的变量,叫类变量静态变量;没有static修饰的是成员变量。

类的静态变量

  • 在内存中只有 一个,java虚拟机在类加载过程中为静态变量分配内存
  • 静态变量位于方法区,被类的所有实例所共享
  • 静态变量可通过类名来访问,其生命周期取决于实例的生命周期。
    实例变量
  • 取决于类的实例。每创建一个实例,java虚拟机会为实例变量分配一次内存。
  • 实例变量位于堆中;其生命周期取决于实例的生命周期。

单例模式的实现

public class Singleton1 {
     
    private static Singleton1 instance=null;//静态私有成员变量

    //私有构造函数
    private Singleton1(){
     }

    //静态公有工厂方法,返回唯一实例
    public static Singleton1 getInstance(){
     
        if(instance==null)
            instance=new Singleton1();
        return instance;
    }
}

测试代码:

public class Client {
     
    public static void main(String[] args) {
     
        Singleton1 s1=Singleton1.getInstance();
        Singleton1 s2=Singleton1.getInstance();
        if(s1==s2){
     
            System.out.println("两个对象是相同实例");
        }else{
     
            System.out.println("两个对象是不同实例");
        }
    }
}

编译运行结果如下:

两个对象是相同实例

由上可看出两次调用getInstance()所获取的对象是同一实例对象,并无法再外部对Singleton实例化。
在单例模式的实现过程中需注意:

  1. 构造函数的可见性为private。
  2. 提供一个类型为自身的静态私有成员变量。
  3. 提供一个公有静态工厂方法

饿汉式单例

类被加载时,静态变量会被初始化,类的私有构造函数会被调用,单例类的唯一实例被创建。

  • 饿汉式:线程安全,调用效率高。示例:
public class Singleton {
     
    //饿汉式
    private static Singleton instance=new Singleton();
    private Singleton(){
     }

    public static Singleton getInstance(){
     
        return instance;
    }
}

特点:

  • 无需考虑多个线程同时访问的问题,可确保实例的唯一性。
  • 从调用速度和响应时间来看,由于单例对象一开始就得以创建,因此优于懒汉式单例。
  • 无论系统运行时是否需要该单例对象,由于在类加载时该对象就创建,资源利用率不及懒汉式单例。
  • 系统加载时创建饿汉式单例对象,使得加载时间会较长

懒汉式单例

懒汉式单例在第一次被引用时实例化,类加载时不会被实例化。

  • 懒汉式:线程不安全。示例:
public class LazySingleton {
     
    private static LazySingleton instance=null;
    
    private LazySingleton(){
     }
    
    public static LazySingleton getInstance() {
     
        if (instance == null) {
     
            instance = new LazySingleton();
        }
        return instance;
    }
}

运行时加载对象,加载类较快,获取对象慢;线程不安全,可加synchronized关键字,但效率低。

  • 懒汉式:双重校验锁。示例:
public class LazySingleton {
     
    private static volatile LazySingleton instance=null;

    private LazySingleton(){
     }

    public static LazySingleton getInstance() {
     
        //第一重判断
        if (instance == null) {
     
            //线程锁定,以处理多个线程同时访问的情况,线程安全
            synchronized (LazySingleton.class){
     
                //第二重判断
                if(instance==null){
     
                    instance=new LazySingleton();
                }
            }
        }
        return instance;
    }
}
  • 第一个instance == null,提高效率:变量使用volatile关键字可以保证可见性
  • 第二个instance == null,为了保证单例:返回是同一个对象。

instance=new LazySingleton();
new对象分解三条指令:
1.分配内存空间
2.初始化对象
3.赋值给变量
假设不加volatile,但能保证变量保证可见性(不保证重排序):若线程A以1-3-2,且执行到3时,另外的线程执行到第一个判断时进不去之后的代码,直接返回,会发生错误

volatile作用:

  • 禁止指令重排序
  • 建立内存屏障

synchronized:

  • 有序性指线程之间运行同步代码块,是按序执行的。

懒汉式特点:

  • 实例在第一次使用时创建,无需一直占用系统资源,实现了延迟加载
  • 必须处理多个线程同时访问的问题,特别是当单例类作为资源控制器
  • 多线程同时引用此类时。需通过双重校验机制进行控制,导致系统性能受影响

单例模式优缺点

优点:

  • 提供了对唯一实例的受控访问,能严格控制客户对实例的访问。
  • 节约系统资源,提高系统的性能
  • 允许可变数目的实例。

缺点:

  • 系统中只有一个实例,导致单例类职责过重,违背了“单一职责原则”
  • 没有抽象层,不利于单例类的扩展。
  • 自动垃圾回收技术,若实例化的共享对象长时间不被利用,会自动销毁并回收资源,下次利用重新实例,导致单例对象状态的丢失。

适用环境

  • 系统只需要一个实例对象,例如:序列号生成器、资源管理器。
  • 客户调用类的单个实例值允许使用一个公共访问点,除了该访问点,不能通过其他途径访问该实例。
  • 数据库连接池。
  • Servlet这种Aplication

Java设计模式之单例模式_第2张图片

你可能感兴趣的:(笔记,设计模式)