单例模式(常见的几种实现方式)

/*
   单例设计模式:是一种创建型设计,让你能够保证一个类只有一个实例,并提供一个
   实例的全局节点。

    问题:
     单例模式解决了两个问题:
        1.保证一个类只有一个实例
        2.为该实例提供一个全局访问节点
            单例模式允许保护该实例不被其他代码覆盖。

     但是单例模式 违反了 单一职责原则
         单一职责原则:类的职责单一,对外值提供一种功能,而引起类变化的原则只有一个。

         饿汉式:类加载就会导致该单实例对象被创建。
         懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象的时候才创建。
 */

 ①.饿汉式

     饿汉式的优缺点:
     缺点:对象的创建是在静态代码块中,也是对类的加载而创建。存在内存的浪费。
     优点: 线程安全。

 第一种实现:
  static  class Singleton_Hungry{
        //1.私有 静态成员变量 来保存 该类的对象
        private static Singleton_Hungry singleton_hungry=new Singleton_Hungry();
        // 2.私有构造方法
        private Singleton_Hungry() {
        }
        //3. 提供一种 静态的方法 获取该对象
        public static Singleton_Hungry getInstance(){
            return singleton_hungry;
        }
    }
    public static void main(String[] args) {
        Singleton_Hungry singleton_hungry=Singleton_Hungry.getInstance();
        Singleton_Hungry singleton_hungry1=Singleton_Hungry.getInstance();
        System.out.println(singleton_hungry==singleton_hungry1);// true
        
    }


第二种实现:

static  class Singleton_Hungry{
    private static Singleton_Hungry singleton_hungry;
    //在静态代码块里面赋值
    static {
        singleton_hungry=new Singleton_Hungry();
    }
    //私有构造方法
    private Singleton_Hungry(){
    }
   // 对外提供 获取该类对象的方法        //实例
    public static Singleton_Hungry getInstance(){
        return singleton_hungry;
    }
}
public static void main(String[] args) {
    Singleton_Hungry instance1 = Singleton_Hungry.getInstance();
    Singleton_Hungry instance2 = Singleton_Hungry.getInstance();
    System.out.println(instance1==instance2);

}
② 懒汉式
 
 第三种实现:

懒汉式:
    第一种:
     线程不安全,但是在 getInstance() 方法上添加了synchronized关键字,,
       导致该方法的执行效果特别低。
        从上面代码可以看出,其实就是在初始化instance的时候出现安全问题,
        一旦初始化完成就不存在了。
static class Singleton_Lazy{
    private static Singleton_Lazy singleton_lazy;
    private Singleton_Lazy(){
    }
    public static synchronized Singleton_Lazy getInstance(){
        if(singleton_lazy==null){

            // 线程1 走到这里,等待
            // 线程2获取到cup 的执行权 ,也会进入到该判断里面
            // 那么 我们创建 的就不是 单线程了
            singleton_lazy=new Singleton_Lazy();
        }
        return singleton_lazy;
    }
}
public static void main(String[] args) {
  Singleton_Lazy singleton_lazy1=Singleton_Lazy.getInstance();
  Singleton_Lazy singleton_lazy2=Singleton_Lazy.getInstance();
    System.out.println(singleton_lazy1==singleton_lazy2);
}

 

双重校验锁:

第四种实现:

/**
 *   我们在讨论一下懒汉式 中加锁的问题, 对于getInstance() 方法来说,绝大数的操作都是读操作,
 *    读操作是线程安全的,所以我们没有必要让每个线程必须 持有锁才能 调用,我们需要
 *    调整加锁的时机。由此也产生了一种新的实现模式:双重校验锁模式。
 *
 */

  /*
    双重校验锁模式是一种非常好的单例模式,解决了单例、性能、线程安全问题
    上面的双重校验检测锁模式看上去完美无缺,
    其实是存在问题的,在 多线程的情况下,可能会出现空指针问题,
    出现问题的原因是JVM 在实例化对象的时候 会出现 优化和指令重排操作
    要解决双重检验锁模式带来的空指针异常的问题,只需使用 volatile关键字,
     volatile 关键字可以保证可见性和有序性。


    不可见性: 并发编程下,多线程修改变量,会出现线程间 变量的不可见性

       变量不可见性内存语义:
               JMM(java memory model) java 内存模型
   */




static class Singleton_Lazy{
    //成员变量
    private static  volatile Singleton_Lazy  singleton_lazy;

    //私有的构造器
    private Singleton_Lazy(){
    }
    public static Singleton_Lazy getInstance(){
        // 第一判断, 如果 singleton_lazy 不为空 不需要抢占锁,直接返回对象
        if(singleton_lazy==null){

            synchronized (Singleton_Lazy.class){
                if(singleton_lazy==null){
                    singleton_lazy=new Singleton_Lazy();
                }
            }

        }
        return singleton_lazy;
    }
}
public static void main(String[] args) {
    Singleton_Lazy instance1 = Singleton_Lazy.getInstance();
    Singleton_Lazy instance2 = Singleton_Lazy.getInstance();
    System.out.println(instance1==instance2);
}

第五种实现 :静态内部类

/*
    静态内部类 单例模式中 实例由 内部类创建,
    由于jvm 在加载外部类的过程中,是不会加载内部类的属性和方法
    被调用的才会被加载,并初始化其静态属性。静态 属性由于被static 修饰
    保证只被实例化一次,并且严格保证实例化顺序。

 */
static  class Singleton_Lazy{

      // 私有 构造器
    private Singleton_Lazy(){
    }
    // 定义 一个静态内部类
    private static class Singleton_LazyHolder{
        // 在 内部类 中 声明 并初始化外部类对象
        private static final Singleton_Lazy SINGLETON_LAZY=new Singleton_Lazy();
    }
    // 提供 公共的访问的方式
    public static Singleton_Lazy getInstance(){
        return Singleton_LazyHolder.SINGLETON_LAZY;
    }
}

public static void main(String[] args) {

    Singleton_Lazy instance1 = Singleton_Lazy.getInstance();
    Singleton_Lazy instance2 = Singleton_Lazy.getInstance();
    System.out.println(instance1==instance2);
}

第六种:枚举方式

/*
    枚举类实现单例模式是极力推荐的单例实现模式,因为枚举是线程安全的
    并且只会装载一次,设计者充分利用了枚举的这个特性来实现单例模式,
    枚举的写法非常简单,而且枚举类型是所用单例实现唯一一种不会破坏的单例实现模式
 */

   

 // 枚举 属于 恶汉式
enum Singleton{
    INTERFACE;
 }

public static void main(String[] args) {

    Singleton singleton1=Singleton.INTERFACE;
    Singleton singleton2=Singleton.INTERFACE;
    System.out.println(singleton2==singleton1);
}

  
   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值