ToB企服应用市场:ToB评测及商务社交产业平台

标题: 一文看懂java单例模式 [打印本页]

作者: 民工心事    时间: 2024-3-2 12:28
标题: 一文看懂java单例模式
​ Java单例模式是一种常用的创建型设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来访问该实例。在Java中,有多种方式可以实现单例模式,下面详细介绍其中的几种常见实现方式。
1什么是创建型设计模式?
处理对象创建的模式,简答的理解就是如何创建对象?很多人都会想到new关键字,一般的项目你到处new是不会有问题的,但是稍微复杂一点的项目,可能需要考虑到对象的单一性、复用性和可扩展性。
 为什么要有单例模式?
单例模式可以提供一种简单而有效的方式来管理和访问全局资源,同时也可以提高系统的性能和可维护性
举一个案例,如果你需要一个全局的缓存(现在好多大数量级的都采用redis),那么这个缓存就只能存在一个实例对象中,理解下。当然总结下来:
2.常见的单例模式

2.1 饿汉式(Eager Initialization)

在这种方式下,单例实例在类加载时就被创建,因此在多线程环境下也能保证只有一个实例。这种方式的缺点是,如果该实例一直没有被使用,会造成内存浪费。
如果看不懂下面代码,就复习下static和final关键字的作用,下面的get方法可有可无,有是因为满足java的封装性,实例变量都是私有静态的,对外提供public的get访问方法。
  1. public class Singleton {
  2.     private static final Singleton instance = new Singleton();
  3.     private Singleton() {}
  4.     //下面相当于是一个get方法,符合封装性
  5.     public static Singleton getInstance() {
  6.         return instance;
  7.     }
  8. }
复制代码
我们复习下static和final关键字:
static关键字用于修饰类的成员(字段、方法、代码块和内部类),表示它们属于类本身,而不是类的实例。所以static不可以用在类方法论里面。下面四条要记住:
使用static关键字的一些常见用途包括:
需要注意的是,静态成员属于类本身,而不是类的实例。因此,它们在内存中只有一份拷贝,并且可以被所有实例共享。同时,静态成员的生命周期与类的生命周期相同,即在类加载时初始化,在程序结束时销毁。
关键字final用于修饰类、方法和变量,具有不同的含义和用法。
final变量可以是基本数据类型,也可以是引用类型。对于基本数据类型的final变量,其值不能被修改。对于引用类型的final变量,其引用不能被修改,但是引用所指向的对象的内容可以被修改。
在Java中,final关键字用于声明一个常量,它可以应用于基本数据类型和引用类型。
对于引用类型的final变量,它的引用不能被修改,意味着一旦引用被赋值后,就不能再指向其他对象。这是因为final变量的引用在内存中是不可变的。
然而,虽然引用不能被修改,但是引用所指向的对象的内容可以被修改。这意味着通过final变量的引用,我们可以修改对象的属性或调用对象的方法,只要这些属性或方法不是被声明为final的。
例如,考虑以下代码片段:
final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
sb是一个final变量,它的引用不能被修改。但是,我们可以通过sb引用来修改StringBuilder对象的内容,将" World"追加到字符串中。
总结起来,对于引用类型的final变量,它的引用不能被修改,但是引用所指向的对象的内容可以被修改。这样的设计可以保护引用不被误修改,同时允许对对象的内容进行修改。
  1. public static void main(String[] args) {
  2.                 final StringBuilder s = new StringBuilder();
  3.                 StringBuilder b = new StringBuilder();
  4.                 StringBuilder c = new StringBuilder();
  5.                 b = c;
  6.                 s = b;
  7.                 /**
  8.                  * The final local variable s cannot be assigned. It must be blank
  9.          *  and not using a compound assignment
  10.                  */
  11.         }
复制代码
final变量在多线程环境下具有线程安全的特性,因为它的值不会被修改。
总结:
2.2懒汉式(Lazy Initialization)

在这种方式下,单例实例在第一次使用时才被创建,以实现延迟加载。这种方式的缺点是,在多线程环境下可能会创建多个实例,需要通过加锁来解决。
下面有的同学就有看不懂了,我来复习下:
java声明一个对象变量(private static Singleton instance;)只是为该变量分配了内存空间,但没有实际创建对象。要创建对象实例,需要使用"new"关键字来调用类的构造方法。
  1. public class Singleton {
  2.     private static Singleton instance;
  3.     private Singleton() {}
  4.     public static synchronized Singleton getInstance() {
  5.         if (instance == null) {
  6.             instance = new Singleton();
  7.         }
  8.         return instance;
  9.     }
  10. }
复制代码
synchronized
synchronized关键字用于修饰方法或代码块,其作用是实现线程的同步,保证多个线程在访问共享资源时的互斥性和可见性。
当一个方法被synchronized修饰时,同一时刻只能有一个线程执行该方法,其他线程需要等待。这样可以避免多个线程同时访问共享资源导致的数据不一致或错误的问题。
具体来说,synchronized修饰方法的作用有以下几点:
需要注意的是,synchronized修饰方法时,锁的粒度是整个方法,即一个线程获取了该方法的锁,其他线程需要等待该线程执行完整个方法才能获取锁。如果方法中只有一小部分代码需要同步,可以考虑使用synchronized代码块来实现更细粒度的锁定。
2.3 双重检查锁(Double-Checked Locking)

这种方式结合了饿汉式和懒汉式的优点,既实现了延迟加载,又保证了线程安全。在第一次创建实例时使用同步锁,以避免多线程环境下创建多个实例。
这种方式其实有点偏底层了,我们知道怎么写就可以。
  1. public class Singleton {
  2.     private volatile static Singleton instance;
  3.     private Singleton() {}
  4.     public static Singleton getInstance() {
  5.         if (instance == null) {
  6.             synchronized (Singleton.class) {
  7.                 if (instance == null) {
  8.                     instance = new Singleton();
  9.                 }
  10.             }
  11.         }
  12.         return instance;
  13.     }
  14. }
复制代码
volatile
volatile关键字用于修饰变量,用来保证变量的可见性和禁止指令重排序。
需要注意的是,volatile关键字只能保证可见性和禁止指令重排序,并不能保证原子性。如果需要保证原子性,可以使用synchronized关键字或者使用原子类(如AtomicInteger)。
总结起来,volatile关键字在多线程编程中起到了重要的作用,可以保证变量的可见性和禁止指令重排序,从而保证了线程之间的正确通信。
2.4 静态内部类(Static Inner Class)

这种方式利用了Java类加载机制的特性,当第一次访问内部类时才会加载并初始化单例实例。这种方式既实现了延迟加载,又保证了线程安全。其实不建议写内部类,看似很装逼,实际很繁琐。
这种模式比较常见。
  1. public class Singleton {
  2.     private Singleton() {}
  3.     private static class SingletonHolder {
  4.         private static final Singleton instance = new Singleton();
  5.     }
  6.     public static Singleton getInstance() {
  7.         return SingletonHolder.instance;
  8.     }
  9. }
复制代码
3.应用场景

单例模式适用于以下场景:
需要注意的是,单例模式可能会引起全局状态的共享和修改,因此在使用时需要谨慎考虑其对系统的影响。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4