java 设计模式之单例模式

  • 相信很多做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;
    }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值