java设计模式——单例设计模式八种方式

绪论

设计模式分为三种类型,共 23 种

  1. 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
  2. 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
  3. 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、 解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)。

单例模式

1、介绍

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

比如 Hibernate 的 SessionFactory,它充当数据存储源的代理,并负责创建Session 对象。SessionFactory 并不是 轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory 就够,这是就会使用到单例模式。

2、单例设计模式八种方式

1. 饿汉式(静态常量)
2.饿汉式(静态代码块)
3.懒汉式(线程不安全)
4.懒汉式(线程安全,同步方法)
5.懒汉式(同步代码块)
6.双重检查
7.静态内部类
8.枚举

2.1 饿汉式(静态常量)

步骤如下:

  1. 构造器私有化 (防止 new )
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法。getInstance
  4. 代码实现
/**
 * 单例模式之 饿汉式(静态变量)
 * 缺点,可能并没有用到这个类,浪费内存。
 */
public class Singleton {

    private Singleton(){

    }

    private final static Singleton singleton = new Singleton();


    public static Singleton getInstance() {
        return singleton;
    }

}

优缺点说明:

  1. 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
  2. 缺点:在类装载的时候就完成实例化,没有达到LazyLoading的效果。如果从始至终从未使用过这个实例,则
    会造成内存的浪费
  3. 这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大 多数都是调用 getInstance 方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静 态方法)导致类装载,这时候初始化 instance 就没有达到 lazy loading 的效果
  4. 结论:这种单例模式可用,可能造成内存浪费。
2.2 饿汉式(静态代码块)
/**
 * 单例模式之 饿汉式(静态代码块)
 * 缺点,可能并没有用到这个类,浪费内存。
 */
public class Singleton {
    private  static Singleton singleton;

    private Singleton(){

    }

    static {
        singleton = new Singleton();
    }


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

这种方式和第一种方式类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执 行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。

2.3 懒汉式(线程不安全)
/**
 * 单例模式之 懒汉式(静态方法)
 * 缺点,存在线程安全问题,不推荐使用
 */
public class Singleton {
    private  static Singleton singleton;

    private Singleton(){

    }

    
    //当处于多线程的情况下,几个线程同时进来,那就失去了单例的效用了。
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

    
}
2.4 懒汉式(线程安全,同步方法)
/**
 * 单例模式之 懒汉式(加锁)
 * 缺点,结局了线程安全的问题,但是每次调用都要进行一次同步判断,很影响效率,不推荐!
 */
public class Singleton {
    private  static Singleton singleton;

    private Singleton(){

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

}
2.5 懒汉式(同步代码块)
/**
 * 单例模式之 懒汉式(代码块加锁)
 * 这种方式并没有起到任何的效果,多线程情况下还是会存在多个对象的情况。
 */
public class Singleton {
    private  static Singleton singleton;

    private Singleton(){

    }

     /**
      * 当线程a, b, c进来的时候,singleton都是null,所以都进到判断里面。
      * 由于加了锁,所以a先进去实例对象后,b和c在外面等着,a出去以后锁释放,b和c先后又继续实例对象了。
      * 所以这种方式并不能做到单例!!!
      **/
    public static  Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }

}

2.6 双重检查
/**
 * 单例模式之 懒汉式(双重检查)
 * 这种方式推荐使用的。
 */
public class Singleton {
    private  static volatile Singleton singleton;  // volatile 数据修改后做一个即时的更新

    private Singleton(){

    }


     /**
      * 当线程a, b, c进来的时候,singleton都是null,所以都进到判断里面。
      * 由于加了锁,所以a先进去实例对象后,b和c在外面等着。
      * a出去以后锁释放,b和c先后进去,但是因为此时singleton已经不是null了,所以直接出去了。
      * 而下次再次调用这个方法的时候,在第一重判断就被拦住了,不用进行同步,又保证了效率。
      **/
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

2.7 静态内部类

/**
 * 单例模式之 静态内部类
 * 好处:
 * 1、当一个类在加载的时候,静态内部类是不会被立即加载的。
 * 2、类在加载的过程中是线程安全的。
 * 这种方式推荐使用的。
 */
public class Singleton {


    private Singleton(){

    }

    private static class InnerSingleton {
        private static final Singleton INSTANCE = new Singleton();
    }


    /**
    * 这时候才去初始化静态内部类
    **/
    public static Singleton getInstance() {
        return InnerSingleton.INSTANCE;
    }

}
2.8 枚举
/**
 * 单例模式之 枚举
 * 避免了多线程同步问题
 * 这种方式推荐使用的。
 */
public class   Singleton {
    
    public static void main(String[] args) {
        SingletonEnum singletonEnum = SingletonEnum.INSTANCE;
        SingletonEnum singletonEnum2 = SingletonEnum.INSTANCE;
//        singletonEnum.sayHello();

        System.out.println(singletonEnum == singletonEnum2); //true 表示两个对象相同
    }

}

enum SingletonEnum {
    INSTANCE;

    public void sayHello() {
        System.out.println("hello~");
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章