`

单例模式的几种写法(包含双检锁写法)

阅读更多

饿汉式单例类

Java代码 
  1. public class Singleton   
  2. {   
  3.     private Singleton(){   
  4.        
  5.     }   
  6.   
  7.     private static Singleton instance = new Singleton();   
  8.   
  9.     private static Singleton getInstance(){   
  10.         return instance;   
  11.     }   
  12. }  
public class Singleton
{
	private Singleton(){
	
	}

	private static Singleton instance = new Singleton();

	private static Singleton getInstance(){
		return instance;
	}
}

 饿汉式提前实例化,没有懒汉式中多线程问题,但不管我们是不是调用getInstance()都会存在一个实例在内存中

内部类式单例类

Java代码 
  1. public class Singleton      
  2. {         
  3.         private Singleton(){      
  4.           
  5.     }      
  6.      
  7.     private class SingletonHoledr(){      
  8.         private static Singleton instance = new Singleton();      
  9.     }      
  10.      
  11.     private static Singleton getInstance(){      
  12.         return SingletonHoledr.instance;      
  13.     }      
  14. }   
public class Singleton   
{      
        private Singleton(){   
       
    }   
  
    private class SingletonHoledr(){   
        private static Singleton instance = new Singleton();   
    }   
  
    private static Singleton getInstance(){   
        return SingletonHoledr.instance;   
    }   
} 

 

内部类式中,实现了延迟加载,只有我们调用了getInstance(),才会创建唯一的实例到内存中.并且也解决了懒汉式中多线程的问题.解决的方式是利用了Classloader的特性.

 

懒汉式单例类

Java代码 
  1. public class Singleton      
  2. {         
  3.     private Singleton(){      
  4.      
  5.     }      
  6.      
  7.     private static Singleton instance;      
  8.     public static Singleton getInstance(){      
  9.         if(instance == null){      
  10.             return instance = new Singleton();      
  11.         }else{      
  12.             return instance;      
  13.         }      
  14.     }      
  15. }    
public class Singleton   
{      
    private Singleton(){   
  
    }   
  
    private static Singleton instance;   
    public static Singleton getInstance(){   
        if(instance == null){   
            return instance = new Singleton();   
        }else{   
            return instance;   
        }   
    }   
}  

 

在懒汉式中,有线程A和B,当线程A运行到第8行时,跳到线程B,当B也运行到8行时,两个线程的instance都为空,这样就会生成两个实例。解决的办法是同步:

可以同步但是效率不高:

Java代码 
  1. public class Singleton      
  2. {         
  3.     private Singleton(){      
  4.      
  5.     }      
  6.      
  7.     private static Singleton instance;      
  8.     public static synchronized Singleton getInstance(){      
  9.         if(instance == null){      
  10.             return instance = new Singleton();      
  11.         }else{      
  12.             return instance;      
  13.         }      
  14.     }      
  15. }    
public class Singleton   
{      
    private Singleton(){   
  
    }   
  
    private static Singleton instance;   
    public static synchronized Singleton getInstance(){   
        if(instance == null){   
            return instance = new Singleton();   
        }else{   
            return instance;   
        }   
    }   
}  

 

这样写程序不会出错,因为整个getInstance是一个整体的"critical section",但就是效率很不好,因为我们的目的其实只是在第一个初始化instance的时候需要locking(加锁),而后面取用instance的时候,根本不需要线程同步。
于是聪明的人们想出了下面的做法:

双检锁写法: 

Java代码 
  1. public class Singleton{   
  2.   private static Singleton single;    //声明静态的单例对象的变量   
  3.   private Singleton(){}    //私有构造方法    
  4.      
  5.   public static Singleton getSingle(){    //外部通过此方法可以获取对象     
  6.     if(single == null){      
  7.         synchronized (Singleton.class) {   //保证了同一时间只能只能有一个对象访问此同步块         
  8.             if(single == null){       
  9.                 single = new Singleton();           
  10.         }      
  11.       }   
  12.     }     
  13.     return single;   //返回创建好的对象    
  14.   }   
  15. }  
public class Singleton{
  private static Singleton single;    //声明静态的单例对象的变量
  private Singleton(){}    //私有构造方法 
  
  public static Singleton getSingle(){    //外部通过此方法可以获取对象  
    if(single == null){   
        synchronized (Singleton.class) {   //保证了同一时间只能只能有一个对象访问此同步块      
            if(single == null){    
                single = new Singleton();        
        }   
      }
    }  
    return single;   //返回创建好的对象 
  }
}

 

 

思路很简单,就是我们只需要同步(synchronize)初始化instance的那部分代码从而使代码既正确又很有效率。
这就是所谓的“双检锁”机制(顾名思义)。
很可惜,这样的写法在很多平台和优化编译器上是错误的。

原因在于:instance = new Singleton()这行代码在不同编译器上的行为是无法预知的。一个优化编译器可以合法地如下实现instance = new Singleton():

1. instance  = 给新的实体分配内存

2. 调用Singleton的构造函数来初始化instance的成员变量

现在想象一下有线程A和B在调用getInstance,线程A先进入,在执行到步骤1的时候被踢出了cpu。然后线程B进入,B看到的是instance  已经不是null了(内存已经分配),
于是它开始放心地使用instance,但这个是错误的,因为在这一时刻,instance的成员变量还都是缺省值,A还没有来得及执行步骤2来完成instance的初始化。

当然编译器也可以这样实现:

1. temp = 分配内存

2. 调用temp的构造函数

3. instance = temp

如果编译器的行为是这样的话我们似乎就没有问题了,但事实却不是那么简单,因为我们无法知道某个编译器具体是怎么做的,因为在Java的memory model里对这个问题没有定义。

双检锁对于基础类型(比如int)适用。很显然吧,因为基础类型没有调用构造函数这一步。

分享到:
评论

相关推荐

    java-单例模式几种写法

    单例模式是软件设计模式中的一种,用于控制类的实例化过程,确保一个类只有一个实例,并提供全局访问点。在Java中,实现单例模式有多种方法,每种方法都有其特点和适用场景。以下是对这六种常见单例模式实现方式的...

    Java设计模式之单例模式的七种写法

    Java设计模式之单例模式的七种写法 单例模式是一种常见的设计模式,它确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机的驱动程序对象常...

    unity中涉及的三种单例模式

    在Unity游戏开发中,单例模式是一种常用的编程设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式在处理需要跨场景共享数据的情况时尤其有用,因为Unity的场景切换可能导致对象被销毁,而单例则...

    01 设计模式之单例模式.pdf

    在Java语言中,实现单例模式主要有以下几种方式: 1. 饿汉式(Eager Initialization) 饿汉式单例模式在类加载的时候就已经进行了实例化,因此它不需要考虑多线程同步的问题。这种方式在类加载时就完成了初始化,...

    简单了解python单例模式的几种写法

    单例模式是一种常用的软件设计模式,在很多编程语言中都有广泛的应用。在Python中实现单例模式不仅有助于提高代码的可读性和可维护性,还能有效控制资源的使用。本文将详细介绍Python中实现单例模式的三种方法:使用...

    objective-c单例模式的完整书写方式

    单例模式的实现通常包括以下几个关键步骤: 1. **声明一个类方法**:`+ (instancetype)sharedInstance;` 这个方法返回单例对象,是全局获取单例的主要途径。 2. **私有化初始化方法**:`- (instancetype)init NS_...

    谈一谈iOS单例模式

    单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。...2、具体单例模式的几种模式 第一种单例模式 //非线程安全写法 static UserHelper * helper = nil; + (UserH

    angular 服务的单例模式(依赖注入模式下)详解

    需要注意的是,虽然提供了`{providedIn: 'root'}`的方式,但文档中明确说明这个属性并不直接与单例模式有关系,它仅仅是服务提供的一种写法。 在实验中,发现即使服务是懒加载的子模块和组件,当通过`app.module....

    C#设计模式.rar

    以下是根据标题和描述中提到的几种设计模式进行的详细解释: 1. **单例模式**:单例模式确保一个类只有一个实例,并提供全局访问点。这种模式在需要频繁创建和销毁对象,且只允许有一个实例的情况下非常有用,如...

    2023年最新java面试大全

    【06期】单例模式有几种写法? 【07期】Redis中是如何实现分布式锁的? 【08期】说说Object类下面有几种方法呢? 【09期】说说hashCode() 和 equals() 之间的关系? 【10期】Redis 面试常见问答 【11期】分布式...

    java技术储备,如何提升自己

    1. 单例模式:了解单例模式的几种写法,包括懒汉式、饿汉式、双重检查锁定式等。 2. JDK 中的设计模式:了解 JDK 中的设计模式,包括 IO 中的装饰模式和设配器模式等。 3. 框架中的设计模式:了解常用的设计模式,...

    春招&秋招面经

    以下是几种实现方式: 1. **懒汉式** ```java public class Singleton { private static Singleton singleton = new Singleton(); private Singleton() {} public static Singleton getSingleton() { ...

    Android SharedPreferences存储的正确写法

    1. **单例模式**:通过`Context.getSharedPreferences()`获取的SharedPreferences对象是单例的,意味着在整个应用中只会有一个实例。这样确保了数据的一致性,避免了多个副本可能导致的冲突。 2. **编辑器模式**:`...

    java_len.zip_java len

    10. **设计模式**:常见的23种设计模式,如单例模式、工厂模式、观察者模式等,是解决常见问题的成熟解决方案。 11. **注解(Annotation)**:用于提供元数据,增强代码的功能和可读性。 12. **Lambda表达式**:...

    design-cart:ES6 设计模式购物车

    这里我们可能会遇到以下几种常见的设计模式: 1. **单例模式**:购物车在整个应用中应当是唯一的,无论用户在哪个页面,购物车的数据都应保持一致。单例模式确保了购物车对象的全局唯一性,避免了多次实例化导致的...

    JAVA 面试题

    6. **单例模式的几种写法:** 单例模式保证一个类只有一个实例,并提供一个全局访问点。常见的单例实现方式有饿汉式、懒汉式、枚举类型以及利用双重检查锁定机制等。 7. **web.xml加载顺序:** 在Web应用中,web....

    天舟通信笔试题1

    单例模式是一种设计模式,确保一个类只有一个实例,并提供全局访问点。常见的单例实现有饿汉式(静态常量)、懒汉式(线程安全的双重检查锁定)和枚举单例等。 5. **递归算法** 递归是函数或方法在定义时调用自身...

    C#面试资料,囊括了个大IT公司招聘时的习题

    10. **设计模式**:熟悉常见的设计模式,如单例模式、工厂模式、观察者模式等,以及它们在实际开发中的应用。 11. **数据库操作**:SQL基础,ADO.NET或Entity Framework的使用,以及数据库事务处理。 12. **Web...

Global site tag (gtag.js) - Google Analytics