更完整的单例模式(java, kotlin)

前言

java 常见的单例模式有三种:

  • 懒汉: getInstance的时候实例化;
  • 饿汉: 引用AA类的时候实例化, 例如 AA.fun() 或者 AA.getInstance();
  • 静态内部类: getInstance的时候实例化, 写法比懒汉要简单;

个人理解:
如果没有除了getInstance 方法之外的 public static fun 的话, 以上三种单例模式在加载时间上基本是没有差别的. 考虑到实现比较轻松, 推荐静态内部类方式创建单例.
如果调用除了getInstance 方法之外的其他静态方法的话, 饿汉模式会创建出单例实例, 而懒汉模式和静态内部类方式(其实也就是懒汉了)不会.


java 静态内部类模式

public class AA {
	private static class Holder{
		private static AA instance = new AA();
	}
	
	private AA(){}
	
	public static AA getInstance(){
		return Holder.instance;
	}
}

注意: 在java中, 静态内部类的加载时机是真正调用到此内部类时才会加载, 所以在调用getInstance方法前单例对象不会被提前创建, 所以是懒汉模式.


java 懒汉模式

  1. 比较简单的写法是这样:

    public class AA {
    	private static AA instance;
    	public static AA getInstance(){
    		if(instance == null){
    			instance = new AA();
    		}
    		return instance;
    	}
    }
    
  2. 但上面这样写不是线程安全的, 如果多线程同时调用 getInstance 时, 有可能同时通过 instance==null 的判断, 导致执行两遍 new AA , 所以需要加锁, 改进写法:

    public class AA {
    	private static AA instance;
    	public static AA getInstance(){
    		if(instance == null){
    			synchronized(AA.class){
    				if(instance == null){
    					instance = new AA();
    				}
    			}
    		}
    		return instance;
    	}
    }
    
  3. 但这样还是有漏洞, jvm 有指令重排机制, 临界情况下的靠后者有可能会得到一个初始化尚未完成的 instance 对象, 这时需要加 volatile 修饰符, 这个修饰符能组织变量访问前后的指令重排, 改进写法:

    public class AA {
    	private volatile static AA instance;
    	public static AA getInstance(){
    		if(instance == null){
    			synchronized(AA.class){
    				if(instance == null){
    					instance = new AA();
    				}
    			}
    		}
    		return instance;
    	}
    }
    

    仔细分析: return instance 依赖于 instance =, 但是有可能指令重排导致new AA()尚未完成就执行完了instance =, 然后后面的return instance依次执行完毕, 而new AA()此时仍未完成, 就会出错了.


kotlin 的单例模式

  • 恶汉 --> 调用AA.getInstance().method()

    class AA private Constructor(){
    	fun method(){
    		// ...  
    	}
    	companion object{
    		@JvmStatic
    		val instance: AA = AA()
    	}
    }
    
  • 懒加载 --> 调用AA.getInstance().method()

    class AA private Constructor(){
    	fun method(){
    		// ...  
    	}
    	companion object{
    		@JvmStatic
    		val instance: AA by lazy { AA() }
    	}
    }
    
  • 极简单例 (也是懒加载)–> java 中调用 AA.INSTANCE.method(), kotlin 中 AA.method()

    object AA{
    	fun method(){
    		// ...  
    	}
    }
    

ps: 虽然 kotlin 的 lazy 文档里提到 返回的是线程安全对象, 但我没有测试(手动滑稽)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值