单例模式的各种写法

14 篇文章 0 订阅
1 篇文章 0 订阅


单例模式概述

什么是单例模式?
确保一个类只有一个实例,并提供该实例的全局访问点。

使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。

私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。


懒汉式-线程不安全

私有静态变量被延迟实例化,这样做的好处是如果没有用到这个类,就不会实例化,从而节约资源。

public class Singleton{
	private static Singleton s;
	private Singleton(){}
	public static Singleton getUnique(){
		if(s==null)
			s = new Singleton();
		return s;
	}
}

饿汉式-线程安全

线程不安全的原因是因为s被实例化多次,如果直接实例化s的话,就不会线程不安全。
但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。

public class Singleton{
	private static Singleton s = new Singleton();
	private Singleton(){}
	public static Singleton getUnique(){
		return s;
	}
}

懒汉式-线程安全

只要对getUnique方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次s。

public class Singleton{
	private static Singleton s;
	private Singleton(){}
	private static synchronized Singleton getUnique(){
		if(s==null){ 
			s = new Singleton();
		}
		return s;
	}
}

但是当一个线程进入该方法后,其他试图进入该方法的线程都必须等待,即使s已经被实例化了。这会让线程阻塞时间过长,因此该方法有性能问题。


双重校验锁-线程安全

s只要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当s没有被实例化时才需要加锁。

双重校验锁先判断s是否已经被实例化,如果没有被实例化,那么才对实例化语句加锁。

public class singleton{
	private volatile static Singleton s;
	private Singleton(){}
	public static Singleton getUnique(){
		if(s==null){ 
			synchronized(Singleton.class){ 
				if(s==null){
					s = new Singleton();
				}
			}
		}
		return s;
	}
}

为什么要使用两个if语句?
在s==null的情况下,如果两个线程都执行了if语句,那么两个线程都会进入if语句块内。虽然在if语句块内有加锁操作,但是两个线程都会执行s = new Singleton()这条语句,只是先后的问题,那么就会进行两次实例化。

第一个if语句用来避免s已经被实例化之后的加锁操作,而第二个if语句进行了加锁,所以只能有一个线程进入,就不会出现s==null时两个线程同时进行实例化操作。

s采用volatile关键字修饰也是很有必要的,s = new Singleton(); 这段代码其实是分三步执行:

  • 为s分配内存空间
  • 初始化s
  • 将s指向分配的内存地址

但是由于JVM具有指令重排的特性,执行顺序有可能变成1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程还没有初始化的实例
getUnique()后发现s不为空,因此返回s,但此时s还未被初始化。
使用volatile可以禁止JVM的指令重排,保证在多线程环境下也能正常运行。


静态内部类实现

加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。

public class Singleton{
	private Singleton(){ 
	}

	private static class SingletonHolder{
		//final变量 只能赋值一次
		//是静态的,随着内部类一起加载
		private static final Sigleton INSTANCE = new Singleton();
	}

	public static Singleton getUnique(){
		return SingletonHolder.INSTANCE;
	}
}

当Singleton类被加载时,静态内部类SingletonHolder没有被加载进内存。

只有当调用getUnique方法从而触发SingletonHolder.INSTANCE时才会被加载,此时初始化INSTANCE实例,并且JVM能确保INSTANCE只被实例化一次。

这种方式不仅具有延迟初始化的好处,而且JVM提供了对线程安全的支持。

也算是一种懒加载。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值