单例模式的五种写法

单例模式

创建唯一类、唯一的对象、有唯一的全局访问点

1,五种写法

1)懒汉式

代码

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

效果1:线程不安全

原因:多个线程并行调用getinstance()的时候,会创建多个实例

解决方案1:将整个getinstance()方法设为同步(synchronized)

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

效果2:做到了线程安全,解决了多实例问题。但不高效,因为任何时候只能有一个线程调用getinstance()方法,但同步操作是只需要在第一次调用时才被需要,也就是说第一次创建实例对象的时候,也就是说当有多个线程时,同步操作对第2个线程始是不起作用的。因此可引出双重检验锁来解决这个问题。

2)双重检验锁

简单来说,就是一种使用同步块加锁的方法,称其为双重检验锁,是因为它有两次检查instance=null ,一次是在同步块外,一次是在同步块内,在同步块内检查就是为了防止多个线程一起进入同步块外的if里,从而造成多个实例的问题。

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

效果3:仍在instance = new Singleton()这里存在一个问题,这并非是一个原子操作,实际JVM进行了3件事。也就是给instance分配内存、调用Singleton的构造函数来初始化成员变量、将instance对象指向分配的内存空间(执行完这步后,instance就为非null了)。但因为JVM的即时编译器中存在指令重排序的优化,所以第2和第3步的执行顺序是不能保证的。当3先执行完后,instance 已经是非空了,但没初始化 ,所以第二个线程将会直接返回instance,然后使用。因此存在多线程问题.

解决方案2:将instance变量声明成volatile即可,volatile具有可见性,在这可以解决JVM的指令重排序优化问题,volatile是禁止这个的。也就是volatile变量的赋值操作后面会有一个内存屏障,读操作不会被重排序到内存屏障之前。

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

效果4:volatile只有在java 5中及之后才可避免JVM重排序问题。因此推出了比较简单的实现线程安全的单例模式的方法,让我们继续往下说。

 

3)饿汉式

public class Singleton(){
		//类加载时就初始化
		private static final Singleton instance = new Singleton();
		
		private Singleton(){}
		
		public static Singleton getInstance(){
			return instance;
		}
}

效果:单例的实例被声明了static和final变量,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。但这不是懒加载模式,因为会在加载类后就初始化,就算客户端没调用getInstance()方法。

4)静态内部类(最好的)

public class Singleton(){
		private static class SingletonHolder{
				private static final Singleton INSTANCE	= new Singleton();
		}
		
		private Singleton(){}
		public static final Singleton getInstance(){
				return SingletonHolder.INSTANCE;
		}
}

5)枚举(非常简单)

public enum EasySingleton{
		INSTANCE;
}

效果:不用担心双重检验锁问题,也能防止反序列化导致重新创建新的对象

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值