单例模式3要素“
1.构造私有,防止外部直接new对象
2.定义静态成员类变量
3.提供静态方法
饿汉式:
public class SingleTon{ private static SingleTon singleTon = new SingleTon(); private SingleTon(){} public static SingleTon getSingleTon(){ return singleTon; } }
优点:
1.线程安全,因为类加载的时候已经完成了单例创建
2.因此获取单例的时候不需要加锁
缺点:
单例的创建不是延迟加载,无论你用与否,都已经被加载,是不是有点浪费资源?
懒汉式:
public class SingleTon{ private static SingleTon singleTon = null; private SingleTon(){} public synchronized static SingleTon getSingleTon(){ if(singleTon==null) singleTon = new SingleTon(); return singleTon; } }
优点:
1.延迟加载,没用到时不加载
2.为了线程安全,我们在获取实例的方法添加了synchronized
缺点:
1.getSingleTon方法为synchronized,无论单例是否已经创建,所有来获取单例的线程都得排队等待,调用频繁的话撞锁严重
双重检测:
该方案对于懒汉式进一步改进,对象存在时直接获取 无需上锁,只有对象不存在需要创建时才进行加锁
public class SingleTon{ private static SingleTon singleTon = null; private SingleTon(){} public static SingleTon getSingleTon(){ if(singleTon==null){ //对象不存在时再创建 synchronized(SingleTon.class){ //对象创建时对该类加锁 if(singleTon==null) { //再次校验是否被创建 singleTon = new SingleTon(); } } } return singleTon; } }
优点:
1.延迟加载,节省资源
2.线程安全
3.撞锁几率小,只有初次创建才会有撞锁可能
问题:
你上面的方案能防止reflection构造对象吗? 不能,如何解决呢?
首先对于reflection构造对象的问题,只需要在默认构造函数在对象存在的情况下被再次调用时抛出异常
public class SingleTon{ private static SingleTon singleTon = null; private SingleTon(){ if(singleTon!=null) throw new Exception("the instance already be created!"); } public static SingleTon getSingleTon(){ if(singleTon==null){ //对象不存在时再创建 synchronized(SingleTon.class){ //对象创建时对该类加锁 if(singleTon==null) { //再次校验是否被创建 singleTon = new SingleTon(); } } } return singleTon; } }
其次能防止反序列化构造对象吗?不能,如何解决呢? 重写readResolve方法!
public class SingleTon{ private static SingleTon singleTon = null; private SingleTon(){ if(singleTon!=null) throw new Exception("the instance already be created!"); } public static SingleTon getSingleTon(){ if(singleTon==null){ //对象不存在时再创建 synchronized(SingleTon.class){ //对象创建时对该类加锁 if(singleTon==null) { //再次校验是否被创建 singleTon = new SingleTon(); } } } return singleTon; }
//这个方法会在反序列化时调用 protected Object readResolve(){ return getSingleTon(); } }
4.静态内部类
java 再创建外部类的时候不会创建内部类实例,只有在外部类使用到内部类的时候才会创建内部类实例
public class SingleTon{ private SingleTon(){} private static class SingleTonInner{ private static final instance = new SingleTon(); } public static SingleTon getInstance(){ return SingleTonInner.instance; } }
难道静态内部类创建就是线程安全的吗?为啥这个时候不需要加synchronized? 这个要涉及到java虚拟机的工作原理,菜鸟暂时不做过多研究。总之多个线程在同时进行静态内部类创建的时候,虚拟机会保证只有一个线程可以执行cinit方法,其他线程都阻塞等待。
5.枚举
还有一种方式就是枚举
public enum SingleTon{ INSTANCE }
那究竟什么时候用到单例呢?
1.网站计数器
2.数据库连接池
3.日志组件
4.多线程的线程池