单例三要素
1.私有的构造函数
2.静态的对象实例
3.静态的获取对象的 方法
第一种:同步获取实例的方法(懒汉式---延迟加载)
public class LazySingleton{ //1.一个私有的指向自己的类变量 private static LazySingleton instance; //2.私有的构造方法,保证不能从外部创建对象 private LazySingleton(){} //3.公开的静态方法,确保在第一次请求实例的时候创建 public static synchronized LazySingleton getInstance(){ if(null == instance){ instance = new LazySingleton(); } return instance; } }
点评:线程安全,但是性能差,每次在获取这个实例的时候都需要进行线程安全判断
第二种:双重检查
public class LazySingleton{ //这里使用volatile修饰,用来保证对象的可见性 private volatile static LazySingleton instance; private LazySingleton(){} public static LazySingleton getInstance(){ if(null == instance){ synchronized(LazySingleton.class){ if(null == instance){ instance = new LazySingleton(); } } } return instance; } }
点评:不需要每次去实例对象,在JDK1.5以前版本不能保证线程安全,存在JDK版本限制
第三种:饿汉式创建对象
public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
点评:直接在实例对象时,静态初始化,保证实例的唯一。但是在某些情况下,对象未被使用,且这个对象很大时,就会很消耗资源
第四种:静态内部类
public class Singleton{ private Singleton(){} static class InstanceHandle{ static volatile Singleton instance = new Singleton(); } public static Singleton getInstance(){ return InstanceHandle.instance; } }
点评:其实这种方式的出现早期是为了解决double-check的问题,其实本质上就是一个懒汉式的做法,还是在类加载时候初始化一个静态实例。玩个花哨而已,而且还变的更加复杂了,想比较第三中没有什么区别!仅仅当做参考而已!
单例中还需要注意的几个点:
1.单例的出现时为了解决在系统中只保证一个对象实例,但是使用的情况应该不多,若是太多的,就是设计存在问题!
2.若是存在多个类加载器的话,那么单例还是会失效。
3.在JDK1.2中,还是会将单例对象回收,需要建立注册表。
4.综合考虑性能和资源,选择合适的方案来解决多线程的问题,默认情况下,我们认为所有的场景都是多线程的!
常见单例实例场景:
线程池,缓存,注册表等!
****** 周边知识联想 ******
Java内存模型详解
一. 内存模型 (memory model)
内存模型描述的是程序中各变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节.
不同平台间的处理器架构将直接影响内存模型的结构.
在C或C++中, 可以利用不同操作平台下的内存模型来编写并发程序. 但是, 这带给开发人员的是, 更高的学习成本.相比之下, Java利用了自身虚拟机的优势, 使内存模型不束缚于具体的处理器架构,
通过Java内存模型真正实现了跨平台.(针对hotspot jvm, jrockit等不同的jvm, 内存模型也会不相同)
内存模型的特征:
a, Visibility 可视性 (多核,多线程间数据的共享)
b, Ordering 有序性 (对内存进行的操作应该是有序的)
二. Java内存模型 ( java memory model )
根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。
每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。
其中, 工作内存里的变量, 在多核处理器下, 将大部分储存于处理器高速缓存中, 高速缓存在不经过内存时, 也是不可见的.
jmm怎么体现可视性(Visibility) ?
在jmm中, 通过并发线程修改变量值, 必须将线程变量同步回主存后, 其他线程才能访问到.
jmm怎么体现有序性(Ordering) ?
通过Java提供的同步机制或volatile关键字, 来保证内存的访问顺序.
详情参见:
http://developer.51cto.com/art/200906/131393.htm
http://lavasoft.blog.51cto.com/62575/222076
http://blog.csdn.net/xymyeah/article/details/3193708
public static final synchronized abstract 的顺序问题:
--可以混用 而且顺序不限