文章目录
前言
本文主要总结了目前常用的几种单例模式的使用方法,包含了Java与kotlin中不同的写法
一、饿汉式
类加载时立即创建单例,即为饿汉式。
java:
java
public class SingletonDemo { //饿汉式,线程安全但有资源浪费情况
private static SingletonDemo INSTANCE = new SingletonDemo(); //结构私有化,静态导致类加载即可构造实例
private SingletonDemo(){}
public static SingletonDemo getINSTANCE() { //外部通过此方法才可调用实例
return INSTANCE;
}
}
kotlin:
kotlin
object SingletonKotlinDemo{} //一个静态对象持有自身的引用,不可有任何构造器,等同线程安全的饿汉式
二、懒汉式
第一次调用时才创建单例,即为懒汉式。
java:
java
public class SingletonDemo { //懒汉式,线程不安全,调用才创建
private static SingletonDemo INSTANCE = null;//静态变量初始为null
private SingletonDemo(){}//结构私有化
public static SingletonDemo getINSTANCE() { //外部调用此方法才会创建对象,多线程同时调用会产生多个实例
if(INSTANCE == null){ //判空防止多次创建
INSTANCE = new SingletonDemo();
}
return INSTANCE;
}
}
kotlin:
kotlin
class SingletonKotlinDemo private constructor(){ //懒汉式,线程不安全,构造私有化
companion object {//kotlin中伴生对象,方法与属性均带有静态作用
private var instance: SingletonKotlinDemo? = null
get() { //上方成员变量的get()方法,field表示存储属性实际值的变量关键字
if(field == null){
field = SingletonKotlinDemo()
}
return field
}
fun getInstance() : SingletonKotlinDemo = instance!!
}
}
三、线程安全的懒汉式
在懒汉式的基础上确保了线程安全
java:
java
public class SingletonDemo { //线程安全的懒汉式,调用才创建
private static SingletonDemo INSTANCE = null;//静态变量初始为null
private SingletonDemo(){}//结构私有化
//此处添加了同步锁保证线程安全,但是多线程使用由于单线路导致耗时长
public static synchronized SingletonDemo getINSTANCE() {
if(INSTANCE == null){ //判空防止多次创建
INSTANCE = new SingletonDemo();
}
return INSTANCE;
}
}
kotlin:
kotlin
class SingletonKotlinDemo private constructor(){ //线程安全的懒汉式,构造私有化
companion object {//kotlin中伴生对象,方法与属性均带有静态作用
private var instance: SingletonKotlinDemo? = null
get() { //上方成员变量的get()方法,field表示存储属性实际值的变量关键字
if(field == null){
field = SingletonKotlinDemo()
}
return field
}
@Synchronized //添加同步锁保证线程安全,但是多线程调用等同于单线程
fun getInstance() : SingletonKotlinDemo = instance!!
}
}
四、静态内部类
使用静态内部类的方式确保的单例,本质上是一种线程安全的懒汉式
java:
java
public class SingletonDemo { //内部静态类,即线程安全的懒汉式,缺点不能传递参数
private static boolean flag = false; //通过标志位防止反射后多次创建实例
// 解决反射调用问题
private Common() {
if (!flag) {
flag = true
} else {
throw new Throwable("SingleTon is being attachked.")
}
}
private SingletonDemo(){}//结构私有化
private static class SingletonHolder{ //静态内部类独立于外部类,不会因为内部类加载而加载
private static SingletonDemo INSTACE = new SingletonDemo(); //只有该类被初始化时才加载
}
public static SingletonDemo getInstacen(){//该方法被调用才会加载holder类,从而生成了SingletonDemo对象
return SingletonHolder.INSTACE;
}
}
kotlin:
kotlin
class SingletonKotlinDemo private constructor() { //静态内部类式,本质是线程安全的懒汉式
// 防止反射破坏单例
init {
if (!flag) {
flag = true
} else {
throw Throwable("SingleTon is being attacked.")
}
}
companion object { //kotlin中伴生对象,方法与属性均带有静态作用
private var flag = false //标志位,防止反射导致多次创建对象
fun getInstance() = SingletonHolder.holder //使用静态方法可保证为懒汉式
}
private object SingletonHolder { //单例,本质是有一个静态成员持有自身的引用
val holder = SingletonKotlinDemo()
}
}
五、双重校验锁式DCL
使用两次判断加上同步锁来解决的单例。
重点:
1.volatile(禁止指令重排序)
volatile本身保证了有序性、可见性、原子性
原理:当每次修改volatile变量后必须立即保存到主内存,而每次使用前则必须从主内存刷新volatile变量的最新值。
原子性:代码的执行过程中会被分成好几个操作,通常需要将变量从主内存读取到该线程的工作内存,进行相应的操作后再保存到主内存,而程序执行是由时间片进行轮转的,所以会导致操作过程被分割,当前线程就失去了操作全部完成的可能性,产生了原子性问题,此时可以通过加锁等手段来保证操作的原子性。
可见性:当一个线程修改了共享变量的值,其他线程可以立即收到最新值即为可见性。但是程序的执行是由当前线程从主内存读取到该线程的工作内存,然后再保存到主内存,其他线程是无法访问该线程的工作内存,因此会有可见性问题,可通过volatile、final等来保证可见性。
有序性:单线程执行过程中是有序的,但是多线程则会有不确定性,多核cpu会在保证执行结果不变与执行顺序不变的情况下进行指令重排序,从而优化运行速度,因为多线程操作共享变量时会因此而产生无法预料的问题,volatile与synchronized可以用来解决该问题。
2.两次判断的原因第一重检查:
防止对象已经创建,没有创建时才会进行下去。第二重检查:
同时有多个线程没有创建对象时,通过同步锁后第一个线程创建完对象,后续线程就无需创建而是直接使用,如果没有该判断,所有没有初始化的线程进入同步锁之后都会创建各自的对象。
不带参版本java:
java
public class SingletonDemo { //线程安全的dcl版本
private SingletonDemo(){}//结构私有化
private static volatile SingletonDemo instance;
public static SingletonDemo getInstance(){
if(instance == null){
synchronized (SingletonDemo.class){
if(instance == null){//没有此判断时,多个null对象的线程进来后都会创建各自的对象
/**
* new 一个对象的过程
* 1.在堆中分配对象内存
* 2.填充对象必要信息+具体数据初始化+末位填充
* 3.将引用指向这个对象的堆内地址
*/
//new对象的过程非原子性,所以volatile防止指令重排序导致其他线程得到未完全初始化的对象
instance = new SingletonDemo();
}
}
}
return instance;
}
}
不带参版本kotlin:
kotlin
class SingletonKotlinDemo private constructor() { //dcl
companion object{
//指定LazyThreadSafetyMode为SYNCHRONIZED模式保证线程安全
val instance : SingletonKotlinDemo by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
SingletonKotlinDemo()
}
}
}
带参数版本kotlin:
kotlin
class SingletonKotlinDemo private constructor(private val values: String) { //dcl可以传递参数
companion object {
@Volatile //禁止指令重排序
private var instance:SingletonKotlinDemo? = null
fun getInstance(values: String) = instance ?: synchronized(this){
instance ?: SingletonKotlinDemo(values).also {
instance = it
}
}
}
}
注:防止序列化导致对象不唯一
重写反序列化时要调用的 readObject 方法
kotlin
private Object readResolve(){
return instance;
}