单例模式的几种写法

今天写一下设计模式之中的单例模式,所谓单例模式其实就是保证在JVM 中一个类仅仅有一个实例。这意味着单例的类不能被new出来,所以我们一般会把构造方法设置为private,提供一个static的getInstace方法让用户调用。


1.懒汉式写法

这种写法是先定义一个instance,在用到的时候才会去new,所以一般称为懒汉式写法。

package com.sgx.singleton;

public class LazySingleton {
	private static LazySingleton  instance;
	private LazySingleton(){}
	public static LazySingleton getInstance(){
		if(instance == null){
			instance = new LazySingleton();
		}
		return instance;
	}
	
}

显然这种写法是非线程安全的,在多线程环境下,会有生成多个实例的情况。两个线程A和B,A判断了instance为null,进行实例的初始化,当A还未初始化完成的时候依然为null,线程B进行了实例的初始化;最终结果会得到两个实例,显然是非线程安全的。


2.饿汉式写法

这种写法会直接new instance出来,不管是否用到先new,所以称为饿汉式写法。

package com.sgx.singleton;

public class HungarySingleton {
	private static final HungarySingleton instance = new HungarySingleton();
	private HungarySingleton(){}
	public static HungarySingleton getInstance(){
		return instance;
	}
	
}
饿汉式是线程安全的写法。


3.线程安全的懒汉式写法

我们知道,方法要线程安全,加上sychronized关键字即可。修改后的线程安全的代码如下:

package com.sgx.singleton;

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

4.双重检验锁写法

第三种写法虽然简单明了,但是很显然效率变低了,因为无论何时他只允许一个线程进行操作,但是想象一下,我们应该限制的其实只是当instance为空的时候,让他加锁,其他情况下是不用加锁的。所以有了双重检验锁的写法,代码如下:

package com.sgx.singleton;

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

为啥叫双重检验锁?第一重,当多线程调用getInstance的时候,先让他们进来,如果instance实例不为空,那么直接return即可,不用加锁,这一点就比第三种写法快很多。这也是双重检验的第一重。第二次检验发生时,假设多个线程通过了第一次检验,线程A拿到锁进入synchronized块,需要对instance实例化,为了防止线程B不知道发生了什么事情,(当A释放掉instance.class的锁,线程B又要获得锁再次初始化,)防止这种事情发生,我们定义instance为volatile,这样当A初始化完,这个instance实例会被刷回主存,对B线程来说她也是可见的,所以B会直接返回instance的实例,而不会发生刚才那种危险的情况,再去new一个instance出来。


5.静态内部类写法

顾名思义,定义内部类,写法如下:

package com.sgx.singleton;

public class Inner {
	private static final class Instance{
		private static final Inner instance = new Inner();
	}
	private Inner(){}
	public static Inner getInstance(){
		return Instance.instance;
	}
}

6.枚举写法
package com.sgx.singleton;

public enum I {
	instance;
	public I open(String box){
		return getSugar(box);
	}
}
因为是枚举类型,实际上反编译后知道是一个继承Enum的类,由于枚举的特性导致只有一个实例,并且是线程安全、反射安全以及反序列化安全。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值