设计模式之单例模式

本文详细介绍了单例模式的概念及其实现方式,包括饿汉式、懒汉式等,并对比了不同实现方式的特点与优劣。重点讲解了懒汉式双重检查加volatile关键字的方式,确保了线程安全的同时也提高了效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 什么是单例模式

  1. 该类只能有一个实例;
  2. 该类能够自动实例化;
  3. 对整个系统可见,即必须向整个系统提供这个实例。

2. 实现

a. 饿汉式

public class Singleton {  

    private static Singleton instance=new Singleton();  

    private Singleton(){  
    }  

    public static Singleton getInstance(){  
        return instance;  
    }  
}  

实现简单,基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,没有进行延迟加载。

b. 懒汉

public class Singleton{  

    private static Singleton instance;  

    private Singleton(){  
    }  

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

lazy loading很明显,但是在多线程时不能正常工作

c. 懒汉 + 同步方法

public class Singleton{  

    private static Singleton instance;  

    private Singleton(){  
    }  

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

解决了多线程同步,但同步范围太大,在实例化instacne后,获取实例仍然是同步的,效率低

d. 懒汉 + 同步块

public class Singleton{  

    private static Singleton instance;  

    private Singleton(){  
    }  

    public static Singleton getInstance(){  

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

缩小了同步范围,但存在创建多个实例的可能,比如线程A和B同时执行到了synchronized(Singleton.class),线程B获得锁执行完之后,线程A获得锁,但是此时没有检查singleton是否为空就直接执行了,所以还会出现两个singleton实例的情况

e. 懒汉 + 双重检查(DCL)

public class Singleton{  

    private static volatile Singleton instance;  

    private Singleton(){  
    }  

    public static  Singleton getInstance(){  

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

instance=new Singleton()并不是原子语句,实际包括了下面三大步骤:
1. 为对象分配内存
2. 初始化实例对象
3. 把引用instance指向分配的内存空间

处理器会进行指令重排序优化,当重排后执行顺序为1,3,2时,若线程1执行到3,此时线程2判断instance!=null,直接返回instance引用,但实际上实例对象还没有初始化完毕,使用它可能会造成程序崩溃。

e. 懒汉 + 双重检查 + volatile

public class Singleton{  

    private static volatile Singleton instance;  

    private Singleton(){  
    }  

    public static  Singleton getInstance(){  

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

使用volatile提供内存屏障,限制处理器进行指令优化重排,保证了instance赋值操作是最后一步完成,不会再出现instance在对象没有初始化时就不为null的情况

f. 懒汉+静态内部类

public class Singleton{  

    private Singleton(){  
    }  
    public static  Singleton getInstance(){  
        return InstanceHolder.instance;  
    }  
    static class InstanceHolder{  
        private static Singleton instance = new Singleton();  
    }  
}  

静态内部类在Singleton类被装载时并不会立即实例化,而是在调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

g. 枚举

public enum EasySingleton {  
    INSTANCE;  
}  

借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值