这里讲解单例模式中的懒汉模式和饿汉模式的注意事项及代码实现
目录
单例模式就是只能有唯一一个实例
什么是饿汉模式呢??
从字面理解上,一个饿汉很着急迫不及待的想吃饭.
举个栗子,如果进行读取10个G的文件,要是用饿汉模式就是一次性的将10个G的文件全部加载完成才可以看. ---->很着急 , 迫不及待
饿汉模式从类加载开始就创建出一个实例--->(很早的就创建实例-->立即加载)
代码实现:
- //饿汉模式
- public class Singleton {
-
- //创建私有静态实例,让类加载时就创建出实例-->饿汉模式
- private static Singleton instance = new Singleton();
-
- private Singleton(){
- //将构造方法设置为私有,保证其他类不能实例化对象(new实例)
- }
-
- //构造方法返回实例
- public Singleton getInstance(){
- return instance;
- }
-
- }
懒汉模式与饿汉模式相反
懒汉模式 很缓慢 ~~ 不急迫~~
还是举个读取文件的栗子~~
还是要读取10个G的文件,懒汉模式是加载屏幕所能容纳的范围,能让用户看到的范围,当翻到下一张的时候,再次进行加载 -->效率很高->用多少~~~加载多少~~~
懒汉模式是 等待需要创建实例时在进行创建--->延迟加载
懒汉模式代码实现:
- //实现懒汉模式
- public class Singleton {
-
- //1.使用volatile保证内存可见性和禁止指令重排序
- //2.先将对象初始化为null,当需要实例化对象时在进行创建
- private volatile static Singleton instance = null;
- //设置构造方法
- private Singleton(){
- //私有构造方法,防止其他类实例化对象
- }
- //获取到实例
- //1.使用双重if是为了降低竞争锁的概率
- public Singleton getInstance(){
- //外层if是用来判断实例有没有初始化好?
- //1.如果已经初始化,不需要竞争锁,不进入if,直接往下执行
- //2.如果没有初始化尝试竞争锁
- if(instance==null){
- //使用synchronized原因是
- //1.防止多个线程同时创建,将读和写打包成一个原子->保证原子性,让其线程竞争锁
- synchronized(Singleton.class){
- //里层if: 当线程拿到锁之后,再来判断是否要真正的创建实例
- if(instance==null){
- instance = new Singleton();//实例化对象
- }
- }
- }
- return instance;
- }
-
- }
使用懒汉模式要注意三点
加锁的目的
因为在创建实例时有写线程在读,有些线程在写,所以很可能导致非原子操作,我们就把读和写打包成1个一个原子-->保证原子性,此时多个线程就要竞争锁
加上volatile关键字的作用
因为有的线程在读有的线程在写,很有可能编译器将其优化成读取寄存器,使得当一个线程修改,而每次读取时不能读取最新值,所以加上volatile
因为要创建实例包含三个操作1.申请内存 2. 在内存上构造出实例 3.将地址赋值给对象的引用
编译器优化时,将2,3顺序颠倒,导致实例化出一个无效的对象,所以加上volatile关键字
双重if的目的
双重if最核心的是降低锁的竞争
里层if是保证实例唯一性,当线程拿到锁之后,判断是否要真正的创建实例,-->只能创建一次
外层if
- public class InnerClassSingleton {
- private InnerClassSingleton(){
- //私有构造方法,防止其他类new对象
- }
- //创建内部类
- private static final class SingletonHolder{
- private static final InnerClassSingleton instance = new InnerClassSingleton();
-
- }
-
- //返回实例
- public InnerClassSingleton getInstance(){
- return SingletonHolder.instance;
- }
-
- }
- public enum EnumSingleton {
- instance;
-
- public EnumSingleton getInstance(){
- return instance;
- }
- }