从无到有,打造完善的单例模式写法

单例模式

  • 单例模式是一种非常常见的设计模式,我们有必要规范地用好单例模式。

  • 单例模式是指,在整个系统中,该单例类只有一个实例对象并始终对外提供同一个实例对象。

单例模式常见的写法有两种,懒汉模式和饿汉模式

  • 懒汉模式:
public class Singleton {
    //在类加载的时候就创建了单例类的对象
    private static final Singleton instance = new Singleton();
    
    //单例模式的无参构造函数,防止外部创建单例类的对象,故为私有的
    private Singleton() {
        
    }
    
    public static Singleton newInstance() {
        //返回为一的单例对象
        return instance;
    }
}
  • 懒汉模式
public class Singleton {
    //将单例类的对象作为静态实例
    private static Singleton instance = null;
    
    //依然是私有的无参构造
    private Singleton {
        
    }
    
    public static Singleton newInstance() {
        //在需要的时候才去创建单例对象
        //如果该对象已经创建,则反之之前创建的对象,不会重新创建
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 懒汉模式的单例模式写法有延迟加载的意思,单例类的实例只有在真正需要用到的时候才创建。反观饿汉模式,单例类的实例是作为该类的一个静态常量存在的,在类初始化时就加载了。
  • 但是,懒汉模式还存在问题,没有考虑多线程下线程安全的问题:***可能会在多个线程并发调用newInstance()方法时,出现多个单例类的实例的问题,这显然是不被允许的。
  • 因此,我们很容易想到用锁的互斥同步的性质实现线程安全,从而解决上述问题,如下:
public class Singleton {
    private static Singleton instance = null;
    
    private Singleton {
        
    }
    
    //使用synchronized为方法加锁
    public static synchronized Singleton newInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 以上写法用重量锁的形式通过互斥同步,使得在执行new Singleton()时只能够一个线程,故能保持单例实例的全局唯一,但同样的,这样会成为系统性能的瓶颈。
  • 为了解决上述问题,就有了双重检查锁定的方式,如下:
public class Singleton {
    private static Singleton instance = null;
    
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        //第一次检测
        if(instance == null) {
            //加锁
            synchronized(Singleton.class) {
                //第二次检测
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • **注意!上述双检测写法是错误的!**由于指令重排优化,可能导致初始化单例对象和将该对象地址赋给instance字段的顺序与上面java代码中书写的顺序不同。
  • 为了解决上述由于指令重排所导致的问题,我们很自然地想到了volatile关键字,如下:
public class Singleton {
    //使用volatile修饰instance变量
    private static volatile Singleton instance = null;
    
    private Singleton {
        
    }
    
    public static Singleton getInstance() {
        if(instance == null) {
            synchronized(Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 上述就是最终的正确的双检测带锁的单例模式的写法。
  • 由于有重量级锁的存在,对性能还是有一定的影响的。故有时候也可采用以下写法:
public class Singleton {
    
    //私有的静态内部类
    private static class SingletonHolder {
        //共有的静态字段,可以通过外部类调用
        public static Singleton instance = new Singleton();
    }
    
    //单例类的私有无参构造
    private Singleton() {
        
    }
    
    //静态方法
    public static Singleton newInstance() {
        return SingletonHolder.isntance;
    }
}
  • 第一次访问类中的静态字段时会触发类加载,并且同一个类只加载一次,类加载过程由类加载器负责加锁,从而保证线程安全。

以上就是对单例模式的总结,循序渐进,慢慢完善。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值