java设计模式:详解单例模式之饿汉式,懒汉式,登记式

1.什么是单例模式

  • 所谓的单例模式,就是设计一个类,它在整个程序中只能有一个该类的实例存在,这就是单例模式。

2.单例模式的三种实现模式

  • 饿汉模式:在类产生的时候就创建好实例,不管需不要需要都去创建实例。这是一种空间换时间的做法。
  • 懒汉模式:不需要创建实例的时候,程序就不再去创建实例;需要用到的时候再去创建。这是一种时间换空间的做法。
  • 登记模式:在需要的时候通过静态内部类去创建实例。

3.单例模式的特征

  • 私有化的构造函数
  • 私有的静态全局变量
  • 共有的静态方法

4.单例模式之饿汉实例

public class HungerSingleton {

    private static final HungerSingleton ourInstance = new HungerSingleton ();

    private HungerSingleton() {
    }

    public static HungerSingleton getInstance() {
        return ourInstance;
    }

    public static void main(String[] args) {
        for (int i=0; i<10;i++){
            new Thread (() ->{
                System.out.println (getInstance ().hashCode ());
            }).start ();
        }
    }

}

控制台输出:

10次一致hashcode

ps:饿汉模式本身线程安全,在类加载的时候,就已经进行实例化。是一种比较占内存的写法。

5.单例模式之懒汉实例----------------线程不安全写法

public class LazySingleton {

    private static final LazySingleton lazySingleton = null;

    private LazySingleton(){

    }

    private static LazySingleton getInstance(){
        if (StringUtils.isEmpty (lazySingleton)){
            lazySingleton = new LazySingleton ();
        }
        return lazySingleton;
    }

    public static void main(String[] args) {
        for (int i=0; i<10;i++){
            new Thread (() ->{
                System.out.println (getInstance ().hashCode ());
            }).start ();
        }
    }

}

控制台输出:

10次不一致的hashcode

ps:在10个线程同时并发去获取实例的时候,有可能都进入了if判断,重新创建了该实例对象。导致线程不安全

6.单例模式之懒汉实例----------------synchronized静态同步方法写法(线程安全,但浪费时间,耗资源

public class LazySingleton {

    private static final LazySingleton lazySingleton = null;

    private LazySingleton(){

    }

    private static synchronized LazySingleton getInstance(){
        if (StringUtils.isEmpty (lazySingleton)){
            lazySingleton = new LazySingleton ();
        }
        return lazySingleton;
    }

    public static void main(String[] args) {
        for (int i=0; i<10;i++){
            new Thread (() ->{
                System.out.println (getInstance ().hashCode ());
            }).start ();
        }
    }

}

控制台输出:

10次一致的hashcode

ps:由于synchronized静态同步方法的锁是当前类对象,只允许一个获得锁的线程访问。在第一个线程进入的时候创建了该实例对象,其他线程进来直接返回了该实例对象,没能进if判断。用此方法如果在获取实例执行时间较长,那么在并发多线程中同时访问是非常耗时,占cpu的操作。

7.单例模式之懒汉实例----------------synchronized同步方法块+double check+volatile关键字写法(线程安全)

public class LazySingleton {

    private static volatile LazySingleton lazySingleton = null;

    private LazySingleton() {

    }

    private static LazySingleton getInstance() {
        if (StringUtils.isEmpty (lazySingleton)) {
            synchronized (LazySingleton.class) {
                if (StringUtils.isEmpty (lazySingleton)) {
                    lazySingleton = new LazySingleton ();
                }
            }
        }
        return lazySingleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread (() -> {
                System.out.println (LazySingleton.getInstance ().hashCode ());
            }).start ();
        }
    }

}

控制台输出:

10次一致的hashcode

ps:使用synchronized同步代码块就必须在里面做一次非空校验,不然在有实例对象的情况下还会在次去创建。volatile防止指令重排序。

8.单例模式之登记模式---------------私有静态内部类写法

public class RegisterSingleton {

    private RegisterSingleton() {

    }

    private static RegisterSingleton getInstance() {
        return Holder.registerSingleton;
    }

    private static class Holder {
         private static final RegisterSingleton registerSingleton = new RegisterSingleton();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread (() -> {
                System.out.println (RegisterSingleton.getInstance ().hashCode ());
            }).start ();
        }
    }

}

控制台输出:

10次一致的hashcode

ps:内部类只有在外部类被调用才加载,产生SINGLETON实例;又不用加锁。此模式有上述两个模式的优点,屏蔽了它们的缺点,是最好的单例模式。

9.总结

饿汉式:本身线程安全

在类加载的时候,就已经进行实例话,无论以后用不用到。如果该类比较占内存,之后没用到,白白浪费了资源。

 

懒汉式:本身线程不安全

需要的时候去创建实例,是一种比较优的写法,但必须上锁,保证线程的原子性。

 

登记式:本身线程安全

内部类只有在外部类被调用才加载,此模式有上述两个模式的优点,屏蔽了它们的缺点,是最好的单例模式。 

java基础:synchronized的实战使用方式

java基础:详解synchronized可重入锁以及死锁的发生

redis:基于redis实现分布式锁,setnx和setex(一)

redis:基于redis实现分布式锁,lua脚本(二)

 

 

你可能感兴趣的:(java)