【设计模式】单例模式

2023-02-27 09:15:58 浏览数 (1)

单例模式

单例模式(Singleton Pattern):确保一个类有且只有一个实例,并提供一个全局访问点。

在开发中,很多时候有一些对象其实我们只需要一个,例如:线程池(threadpool)缓存(cache)默认设置注册表(registry)日志对象等等,这个时候把它设计为单例模式是最好的选择。

Java中单例模式是一种广泛使用的设计模式,单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间(比如spring管理的无状态bean);还能够避免由于操作多个实例导致的逻辑错误。如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。

懒汉模式(线程不安全)

代码语言:javascript复制
/**
 * @author inke219223m
 */
public class Singleton {

    private static Singleton instance;
    private Singleton_01() {}

    public static Singleton getInstance() {
        if (null != instance) return instance;
        instance = new Singleton();
        return instance;
    }

}

懒汉模式(线程安全)

此模式保证了线程安全,但是由于把锁加到了方法上,所有的访问都因为需要锁占用导致资源浪费。

代码语言:javascript复制
/**
 * @author inke219223m
 */
public class Singleton {

    private static Singleton instance;
    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (null != instance) return instance;
        instance = new Singleton();
        return instance;
    }

}

双重校验锁(懒汉)

代码语言:javascript复制
/**
 * @author inke219223m
 */
public class Singleton {
    
    private volatile static Singleton singleton;  
    private Singleton (){}  
    
    public static Singleton getSingleton() {  
     if (singleton == null) {  
         synchronized (Singleton.class) {  
          // 注意此处还得有次判空~
          if (singleton == null) {  
              singleton = new Singleton();  
          }  
         }  
     }  
     return singleton;  
    }
    
}  

饿汉模式(线程安全)

此模式在程序启动时直接运行加载,后续有外部需要使用的时候获取即可。无论程序中是否用到这样的类都会在程序启动之初创建。(这也是它的缺点,无意义地占用内存)

代码语言:javascript复制
/**
 * @author inke219223m
 */
public class Singleton {

    private static Singleton instance = new Singleton();
    public Singleton(){};

    public static Singleton getInstance() {
        return instance;
    }

}

饿汉(变种)

代码语言:javascript复制
/**
 * @author inke219223m
 */
public class Singleton {  
    
    private static Singleton instance = null;  
    
    static {  
      instance = new Singleton();  
    }  
    
    private Singleton (){}  
    
    public static Singleton getInstance() {  
     return instance;  
    }
    
} 

类的静态内部类

使用类的静态内部类实现的单例模式,保证了线程安全,也保证了懒加载,也不会因为加索的方式耗费性能,这主要是因为JVM虚拟机保证多线程并发访问正确性,一个类的构造方法在多线程环境可以被正确加载(推荐使用)。

代码语言:javascript复制
/**
 * @author inke219223m
 */
public class Singleton {

    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }

    private Singleton() {
    }

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

CAS[AtomicReference](线程安全)

Java并发库提供了很多原子类支持并发访问数据安全性,AtomicReference可以封装引用一个实例,支持并发访问。使用CAS的好处就是不需要使用传统的加锁方式保证线程安全,而是依赖于CAS的忙等算法,依赖于底层硬件的时间。相对于其他锁的实现没有线程的切换和组测也就没有了额外的开销,可以支持比较大的并发性,缺点就是如果一直没有获取到将会处于死循环中。

代码语言:javascript复制
/**
 * @author inke219223m
 */
public class Singleton {

    private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

    private static Singleton instance;

    private Singleton() {
    }

    public static final Singleton getInstance() {
        for (; ; ) {
            Singleton instance = INSTANCE.get();
            if (null != instance) return instance;
            INSTANCE.compareAndSet(null, new Singleton());
            return INSTANCE.get();
        }
    }

}

枚举单例(线程安全)

这种方式在功能上与共有域方法相近,但更加简洁,无偿提供串行化机制,绝对防止对此实例化(但是在继承场景下不可用)。

代码语言:javascript复制
/**
 * 单元素的枚举类型已经成为实现Singleton的最佳方法
 * @author inke219223m
 */
public enum Singleton {

    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }
}

class MainTest {

    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }

}

项目地址:https://gitee.com/codingce/codingce-leetcode

0 人点赞