设计模式之单例模式

2020/04/21 设计模式

前言

在开发过过程中会使用到单例模式,常见的单例模式有以下几种:

  • 饿汉模式
  • 饿汉模式静态代码块
  • 懒汉模式
  • 懒汉同步方法模式
  • 懒汉方法内同步方式
  • 懒汉双重检查模式
  • 内部类模式
  • 枚举模式

1. 饿汉模式

public class HungryMode {
    private HungryMode(){}

    private static HungryMode lazyMode = new HungryMode();

    public static HungryMode getLazyMode() {
        return lazyMode;
    }

    public static void main(String[] args) {
        HungryMode lazyMode = getLazyMode();
        System.out.println(lazyMode);
    }
}

1.1 优点

这种类型的饿汉模式是线程安全的,通过jvm类加载机制确保了线程安全

1.2 缺点

饿汉模式采用了静态变量,类装载的时候就创建了对象,没有实现对象的懒加载可能会造成对内存的浪费

2.饿汉模式静态代码块

public class HungryStaticMode {

    private HungryStaticMode(){}

    private static HungryStaticMode instance;

    static {
        instance = new HungryStaticMode();
    }

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

2.1 优点

这种类型的饿汉模式是线程安全的,通过类装载确保了线程安全

2.2 缺点

类装载的时候就会加载静态代码块时,就创建了对象,无法实现懒加载,可能会造成对内存的浪费

3 懒汉模式

懒汉模式是在静态变量创建对象默认没有进行赋值,然后获取实例进行非空判断。

public class LazysMode {

    private LazysMode(){}

    private static LazysMode hungryMode;

    public static LazysMode getHungryMode() {
        if (hungryMode == null){
            hungryMode = new LazysMode();
        }
        return hungryMode;
    }

    public static void main(String[] args) {
       /* for (int i =0; i<100;i++){
            HungryMode hungryMode = HungryMode.getHungryMode();
            System.out.println(hungryMode);
        }*/

        for (int i = 0; i< 1000; i ++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    LazysMode hungryMode = LazysMode.getHungryMode();
                    System.out.println(hungryMode);
                }
            }).start();
        }

    }

}

3.1 优点

懒汉模式在调用实例的时候在创建对象,实现了实例的懒加载。

3.2 缺点

在多线程环境下,线程不安全。

4 懒汉同步方法模式

懒汉同步方法模式就是在懒汉模式的基础上在获取实例方法中加上synchronized关键字,获取实例方法阻塞,从而实现线程安全。

public class LazySyconizedMode {

    private LazySyconizedMode(){}

    private static LazySyconizedMode lazySyconizedMode;

    /**
     *  加入synchronized 悲观锁
     *  同步,线程便安全
     *  同步会进行阻塞
     * @return
     */
    public static synchronized LazySyconizedMode  getLazySyconizedMode() {
        if (lazySyconizedMode == null)
            lazySyconizedMode = new LazySyconizedMode();
        return lazySyconizedMode;
    }
}

4.1 优点

加入synchronized, 通过synchronized关键字阻塞方法,实现方法的同步,实现了线程安全

4.2 缺点

使用synchronized关键字锁住整个方法,方法调用效率变低。

5 懒汉方法内同步方式

懒汉方法内同步方式是在懒汉模式的基础上在创建对象实例时,执行同步方法添加 synchronized 关键字

public class LazySynchronizedMode {
    private LazySynchronizedMode(){}

    private static LazySynchronizedMode lazySynchronizedMode;

    public static synchronized LazySynchronizedMode getLazySyconizedMode() {
        if (lazySynchronizedMode == null){
            synchronized (LazySyconizedMode.class){
                lazySynchronizedMode = new LazySynchronizedMode();
            }
        }
        return lazySynchronizedMode;

    }
}

5.1 缺点

非线程安全, 可能再多线程情况下产生多个实例 不能使用

6 懒汉双重检查模式

使用volatile 关键字实现了线程变量的共享

  • 1、进行了两次 if null 的判断
  • 2、双重检查避免了多次同步,
  • 3、使用延迟加载提供了效率
  • 4、这种模式实现了线程安全
 public class LazyDoubleCheckMode {

    private static volatile LazyDoubleCheckMode lazyDoubleCheckMode;

    private LazyDoubleCheckMode(){}

    public static LazyDoubleCheckMode getLazyDoubleCheckMode() {
        if (Objects.isNull(lazyDoubleCheckMode)){
            synchronized (LazyDoubleCheckMode.class){
                if (Objects.isNull(lazyDoubleCheckMode)){
                    lazyDoubleCheckMode = new LazyDoubleCheckMode();
                }
            }
        }
        return lazyDoubleCheckMode;
    }
}

6.1 优点

懒汉模式 双重检查,线程安全,实现了懒加载,方法比较优秀,比较推荐使用

7 内部类模式

通过内部类的模式创建对象实例和返回实例

public class InnerClassMode {
    private static class SingletonHolder{
        private static final InnerClassMode INSTANCE = new InnerClassMode();
    }
    private InnerClassMode(){}

    public static InnerClassMode getInstance() {
        return SingletonHolder.INSTANCE;

    }

    public static void main(String[] args) {
        System.out.println(getInstance());
    }
}

7.1 优点

类被装载的时候内部类并不会立即被装载,所以支持懒加载的,线程安全+懒加载 推荐使用

8 枚举模式

使用枚举模式实现单利模式

/**
 * 单例模式
 */
public enum SingletonEnum {
    INSTANCE;

    public static SingletonEnum testWhatever(){
        return SingletonEnum.INSTANCE;
    }

    public static void main(String[] args) {
        System.out.println(testWhatever());
    }
}

总结

在平常的开发过程中,推荐使用双重检查模式和内部类模式实现单例模式,这两种模式同时实现了线程安全和懒加载。

Search

    Table of Contents