- 相信很多做java的童鞋都听说过.但是一下子让他们写出来.即便是2-3年工作经验的人,也不一定能写出来一个可用的单例模式.所以我们今天来学习学习单例模式
通常有俩种模式
立即加载-饿汉模式: 立即加载就是使用的时候对象已经实例化
public class MyObject(){
private static MyObject = new MyObject();
…………………….
}- 延时加载-懒汉模式 就是调用get()方法时,对象才被实例
单例模式:
public class SimpleSingle {
/*持有 私有 静态 实例 防止被应用
* -延时加载懒汉模式 就是对象调用时 对象还没有创建
* 立即加载-饿汉模式 = new object()
*
* */
private static SimpleSingle instance = null;
/* 私有构造方法 防止被实例化 */
private SimpleSingle() {
}
/**
* 这里没有同步代码块.所以是线程不安全的
* @return
*/
public static SimpleSingle getIntance(){
if(instance == null){
instance = new SimpleSingle();
}
return instance;
}
/**如果该对象用于序列化 可保持 序列化前后一致**/
public Object readResolve(){
return instance;
}
/**
* 线程安全的
* 如果是将synchronized 加到方法上 就是锁了这个对象,效率低下 ,因为只需要在第一次创建的时候加锁 之后就不需要了
*
*/
public static synchronized SimpleSingle getInstance1(){
if(instance == null){
instance = new SimpleSingle();
}
return instance;
}
/**
* 使用同步代码块 创建 单例对象
* @return
*/
public static SimpleSingle getInstance3(){
if(instance == null){
synchronized (SimpleSingle.class) {
if(instance == null){
instance = new SimpleSingle();
}
}
}
return instance;
}
}
getIntance3() 方法看似没有什么问题
将 synchronized 关键字加在了内部,也就是说当调用的时候是不需要加
锁的,只有在 instance 为 null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,
还是有可能有问题的,看下面的情况:在 Java 指令中创建对象和赋值操作是分开进行的,也就是说
instance = new Singleton();语句是分两步执行的。但是 JVM 并不保证这两个操作的先后顺序,也就是
说有可能 JVM 会为新的 Singleton 实例分配空间,然后直接赋值给 instance 成员,然后再去初始化这
个 Singleton 实例。这样就可能出错了,我们以 A、 B 两个线程为例:
a>A、 B 线程同时进入了第一个 if 判断
b>A 首先进入 synchronized 块,由于 instance 为 null,所以它执行 instance = new Singleton();
c>由于 JVM 内部的优化机制, JVM 先画出了一些分配给 Singleton 实例的空白内存,并赋值给 instance
成员(注意此时 JVM 没有开始初始化这个实例),然后 A 离开了 synchronized 块。d>B 进入 synchronized 块,由于 instance 此时不是 null,因此它马上离开了 synchronized 块并将结果
返回给调用该方法的程序。
e>此时 B 线程打算使用 Singleton 实例,却发现它没有被初始化,于是错误发生了。
实际情况是,单例模式使用内部类来维护单例的实现, JVM 内部的机制能够保证当一个类被加载的时
候,这个类的加载过程是线程互斥的。这样当我们第一次调用 getInstance 的时候, JVM 能够帮我们保
证 instance 只被创建一次,并且会保证把赋值给 instance 的内存初始化完毕,这样我们就不用担心上
面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们
暂时总结一个完美的单例模式:
public class Singleton {
/* 私有构造方法,防止被实例化 */
private Singleton() {
}
/* 此处使用一个内部类来维护单例 */
private static class SingletonFactory {
private static Singleton instance = new Singleton();
}
/* 获取实例 */
public static Singleton getInstance() {
return SingletonFactory.instance;
}
/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
return getInstance();
}
}
世面上有很多很多的单例模式..但是我还是喜欢自己写的这个双重检测的单例模式 double -check -locking
使用dcl 双重检测来实现单例模式.
/**
* 鉴于之前写的多线程单例模式有问题.
* 这次我写一个dcl 双重检查 单例模式
* ---doble chek locking
*/
public class MyObject {
//静态实例防止被实例
private volatile static MyObject myObject;
private MyObject() {
}
/**
* 使用双检测机制来解决问题..既不需要同步代码的异步性.
* 也可以保持单例.
* @return
*/
public static MyObject getInstance(){
try {
if (myObject != null) {
}else{
// Thread.sleep(3000);
synchronized (MyObject.class) {
if(myObject == null){
myObject = new MyObject();
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return myObject;
}
}