设计模式(一)--->单例模式(Singleton)

一:单例模式

1.简介

单例模式属于创建型模式,提供了创建对象的最佳方式,这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,并且该类提供了访问其唯一对象的方式

总的来说:

  • 单例类只能有一个实例(static)
  • 单例类只能自己创建自己的唯一实例(构造方法private修饰,参考enum类)
  • 单例必须给其他对象提供自己访问的实例(通过自定义的public方法获取实例)

2.从需求目的的粗浅理解单例

  • 意图: 保证一个类仅有一个实例,并提供它的全局访问点。
  • 目的: 避免使用频繁的类频繁的创建和销毁 (spring默认创建bean是单例)
  • 缺点: 没有接口,没有继承。

3.饿汉式实现单例模式

package com.htu;

public class Singleton {


    //创建一个Singleton对象
    private static Singleton instance = new Singleton();


    //保证私有,这样该类就不可以被实例化
    private Singleton(){
    }

    //有一个唯一可以访问对象的方法
    public static Singleton getInstance(){
        return instance;
    }

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

//调用
    public static void main(String[] args) {
        // 正确获取唯一单例方式
        Singleton instance = Singleton.getInstance();

        // 用单例做一些事情
        instance.doSomething();
    }

}

4.懒汉式实现单例模式(不安全)

  • 线程不安全
  • 延时加载(用到时再加在,避免浪费资源)
  • 严格上不算单例
package com.htu;

public class LazySingleton {

    //创建一个对象  延时初始化
    private static LazySingleton lazySingleton;

    //构造方法私有,保证自身初始化
    private LazySingleton(){ }

    //提供一个唯一的访问方法
    public static LazySingleton getInstance(){
        // 此处会有线程问题,如果线程A和线程B都执行到了步骤1,并未完成步骤2,那么就会创建多个实例
        if(lazySingleton == null){
            return new LazySingleton();
        }
        return lazySingleton;
    }

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

5.安全懒汉式实现单例模式

  • 线程安全
  • 懒加载
class SafeLazySingleTon{

    //创建一个对象  演示初始化
    private static SafeLazySingleTon safeLazySingleTon;

    //构造方法私有 保证只会被自己实例化
    private SafeLazySingleTon(){}

    //提供一个唯一访问的这个实例的方法
    public static synchronized SafeLazySingleTon getInstance(){
        if(safeLazySingleTon == null){
            return new SafeLazySingleTon();
        }
        return safeLazySingleTon;
    }

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

}

6.双检查锁实现安全懒汉式

上面getInstance 加锁,此时可以保证安全性,如果这个类访问次数较多会导致性能下降问题。我们实现双检查锁来缩小同步代码块的范围,用volatile来确保执行顺序不被打乱(指令重排序)

class TwoSafeLazySingTon{
    //使用volatile关键字禁止指令重排
    private static volatile TwoSafeLazySingTon twoSafeLazySingTon;

    //构造方法私有,只能被自己实例化
    private TwoSafeLazySingTon(){

    }

    //外部唯一可访问的对象
    public static TwoSafeLazySingTon getInstance(){

        //判断是不是单例的(步骤1)
        if(twoSafeLazySingTon == null){
            //锁住这个类,缩小范围(步骤2)
            synchronized (TwoSafeLazySingTon.class){
                //如果在锁住这个类的同时有线程执行到这里  我们需要判断执行完没有
                if(twoSafeLazySingTon == null){
                    return new TwoSafeLazySingTon();
                }
            }
        }

        return twoSafeLazySingTon;
    }

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

7.静态内部类

上面我们发现它不是懒加载,是饿汉式加载。我们为了实现懒加载还有另外一种思路。调用静态内部类,用 classloader 机制来保证初始化 instance 时只有一个线程。

class StaticSingTon{

    //设置一个静态内部类,调用这个内部类加载内容
    private static class PrivateSingle{
        //用final修饰  让其不可以改变
        private static final StaticSingTon INSTACE = new StaticSingTon();
    }

    //私有构造方法,只有自己可以实例化
    private StaticSingTon() {

    }

    //这里直接初始化这个静态内部类
    public static StaticSingTon getInstance(){
        //此时直接返回这个静态内部类的结果即可
        return PrivateSingle.INSTACE;
    }

}
posted @ 2022-04-29 14:15  Diamond-fz  阅读(2476)  评论(1编辑  收藏  举报