单例模式官方定义
确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一的实例。
单例模式的实现机制
实现单例模式的思路是,为了防止客户程序利用构造方法创建多个对象,将构造方法声明为private类型。其原因是,如果构造方法为public类型,则客户程序永远可以通过使用改构造方法创建不同的对象。但这样做的问题是,如果一个类的构造方法是private的,则其他类无法使用该构造方法来创建对象,从而该类就成为不可用的了。为了解决此问题,该类必须提供一个实例方法,通常声明为getinstance方法。该方法返回一个实例。该方法为静态的,否则客户程序无法调用。
Volatile变量
在程序设计中,尤其是在C语言、C++、C#和Java语言中,使用volatile关键字声明的变量或对象通常具有与优化、多线程相关的特殊属性。通常,volatile关键字用来阻止(伪)编译器认为的无法“被代码本身”改变的代码(变量/对象)进行优化。如在C语言中,volatile关键字可以用来提醒编译器它后面所定义的变量随时有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
在C环境中,volatile关键字的真实定义和适用范围经常被误解。虽然C++、C#和Java都保留了C中的volatile关键字,但在这些编程语言中volatile的用法和语义却大相径庭。
在C,以及C++中,volatile关键字的作用[1]:
- 允许访问内存映射设备
- 允许在
setjmp
和longjmp
之间使用变量 - 允许在信号处理函数中使用sig_atomic_t变量
根据相关的标准(C,C++,POSIX,WIN32)和目前绝大多数实现,对volatile变量的操作并不是原子的,也不能用来为线程建立严格的happens-before关系。volatile
关键字就像便携式线程构建一样基本没什么用处[2][3][4][5][6]。
Visual C++ 2005 保证volatile变量是一种内存屏障,阻止编译器和CPU重新安排读入和写出语义。[7] 在先前版本的Visual C++则没有此类保证。在其他方面将指针定义为volatile可能会影响程序的性能。例如,如果指针定义对代码的其他地方可见,强制编译器将指针视为屏障,就会降低程序的性能,这是完全不必要的。
对用户定义的非基本数据类型使用volatile
基本类型的对象用volatile修饰后,仍旧支持所有的操作(加、乘、赋值等)。但是,用户定义的非基本类型(class、struct、union)的对象被volatile修饰后,具有不同行为:
- 只能调用volatile成员函数;即只能访问它的接口的子集。
- 只能通过const_cast运算符转为没有volatile修饰的普通对象。即由此可以获得对类型接口的完全访问。
- volatile性质会传递给它的数据成员。
代码如下
1 public class Singleton { 2 private static volatile Singleton instance=null; 3 private Singleton(){ 4 5 } 6 7 //静态工厂方法 8 public static Singleton getInstance(){ 9 if(instance==null){ 10 synchronized (Singleton.class){ 11 if(instance==null){ 12 13 instance=new Singleton(); 14 } 15 } 16 } 17 return instance; 18 } 19 } 20 21 22 package com.wanson.designpatten; 23 24 import java.util.Calendar; 25 26 public class Driver { 27 public static void main(String[] args) { 28 Singleton instance=Singleton.getInstance(); 29 System.out.println(instance); 30 Singleton secondInstance=Singleton.getInstance(); 31 System.out.println(secondInstance); 32 Calendar calendar1= Calendar.getInstance(); 33 System.out.println(calendar1); 34 Calendar calendar2=Calendar.getInstance(); 35 System.out.println(calendar2); 36 } 37 38 }