首页 > 其他 > 详细

单例模式的几种实现方法

时间:2021-04-13 10:32:38      阅读:23      评论:0      收藏:0      [点我收藏+]

今天面试的时候面试官让我写一个单例模式,然后我就写了一个双重检验锁的单例模式,但是在问如果去掉一个验证后为什么不行的时候,我当时说的有点乱七八糟的,很是不好,重新整理之后,我发现我竟然写错了好几个地方的代码。所以我就想记录一下这个问题,实现单例模式的几种方法。

1. 饿汉式创建单例模式

饿汉式创建单例模式,就是在类初始化的时候就已经建立好了单例。使用final修饰符进行修饰,表示这是一个静态的变量,初始化赋值之后就不能再次赋值了。

public class Singleton{
    public final static Singleton instance = new Singleton();
    
    public static Singleton getInstance(){
        return instance;
    }
}

2. 懒汉式创建单例模式

双重验证方式

也就是懒加载模式,只有在使用的时候才会进行加载。必须保持线程的初始化。
该方法中使用的volatile修饰instance,当instance被修改后,其他的线程也可以立马获取变量。使用synchronized锁来锁定该字段,并且锁定代码块,确定只有一个线程能够进入该代码块。第2重验证防止其他线程提前进入了synchronized同步块后已经实例了该对象,而本线程又重复进入了该同步代码块,所以需要再次进行验证。

volatile: 一定要记得把java的关键字全部要写一下,千万不要写错了,我在面试的时候就把这个单词写错了。。。。。
如果没有第二重验证会怎么样?

答案是:如果有两个线程同时竞争synchronized修饰的代码块的时候,就会有instance被实例化了两次,这个时候就会有instance指向了两个不同的对象。这就违背了单例模式的本意,所以需要进行两次验证。

注意:在静态方法中,synchronized方法不能使用synchronized(this),只能使用synchronized(Singleton.Class)方法,只能锁定整个方法区域。

public class Singleton{
  public static volatile Singleton instance = null;

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

同步静态代码块

使用同步方法直接加在了方法上,这种方法保证了线程的安全性。但是这种方法的性能要比双重验证的方法性能要低,是因为双重验证方法中,只有第一次判断instance为null的时候才会进入同步方法块,其他的线程才会被阻塞。但是使用同步静态代码块中每一次获取实例,都会进入同步代码块,如果有多个线程同时获取,那么只有一个线程能够获取,其他的线程都会被堵塞,这样就会降低程序的性能。

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

内部静态类实现的单例模式

这种方法通过java的定义实现了同步,并且

  • SingleHolder是静态内部类,只有Singleton内部的方法才能够访问,所以说他是懒汉式的。
  • JVM本身的机制确保了之中方法是线程安全的
  • 同时读取实例也不会存在同步问题,也不存在JDK版本的问题,所以推荐这种方法作为懒汉式获取方法
public class Singleton{
   private static class SingleHolder{
        public static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){
        return SingleHolder.INSTANCE;
    }
}

3. 通过枚举 Enum

  • 通过枚举的方式创建的实例更加简单,而且创建枚举类本身就是线程安全的
  • 还能防止反序列化重新创建对象
  • 缺点是无法进行懒加载
    调用方式为EasySingleton.INSTANCE
public enum EasySingleton{
    INSTANCE;
}

完整的代码为:

public class Singleton{
  // 使用枚举的方法创建单例模式
    public enum EasySingleton{
        INSTANCE;
        private final Singleton instance;

        EasySingleton() {
            instance = new Singleton();
        }

        public Singleton getInstance(){
            return instance;
        }
    }

    // 获取静态实例的方法
    public static Singleton getInstance(){
        return EasySingleton.INSTANCE.getInstance();
    }
}

单例模式的几种实现方法

原文:https://www.cnblogs.com/liulongtao/p/14651178.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!