【设计模式】单例模式的绝对单例及线程安全

1. 饿汉模式

/**
 * 饿汉模式
 * @author 郑海鹏
 * @since 2015年7月6日
 */
public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }

    public static void func() {
        System.out.println("this is a function!");
    }
}

优点

线程安全、绝对单例

缺点:

  1. 在多实例或者有其他静态方法时,instance在没有使用它的时候就已经加载好了。
    例如调用Singleton.func(),这个方法中并没有用到instance,但是instance也被初始化了。若该对象很大,则在没使用时就加载很浪费内存。
  2. 不能防止反序列化、反射产生新的实例。

2. 懒汉模式,延时加载

/**
 * 懒汉模式
 * @author 郑海鹏
 * @since 2015年7月6日
 */
public class Singleton {
    private static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (null == instance) { // ①
            instance = new Singleton();
        }
        return instance;
    }

}

优点:

instance在没有使用时不用加载,节省内存。

缺点:

  1. 线程不安全;
    线程不安全原因:当多个线程执行到①时,都是发现instance为null,于是都去初始化了。
  2. 不能防止反序列化、反射产生新的实例。

3. 方法锁

/**
* 方法锁
* @author 郑海鹏
* @since 2015年7月6日
*/
public class Singleton {
    private static Singleton instance = null;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        if (null == instance) {
            instance = new Singleton();
        }
        return instance;
    }

}

优点:

线程安全,绝对单例

缺点:

  1. 加锁导致程序并发性降低;
  2. 不能防止反序列化、反射产生新的实例。

4. 双重检查锁 DCL

/**
* 双重检查锁DCL
* @author 郑海鹏
* @since 2015年7月6日
*/
public class Singleton {
    private static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {

        if (null == instance) { // ①
            synchronized (Singleton.class) {
                if (null == instance) { // ②
                    instance = new Singleton(); // ③
                }
            }
        }
        return instance;
    }

}

优点:

大部分时候线程安全,相比方法锁程序并发性较高。

缺点:

  1. 线程并非绝对安全;
  2. 不能防止反序列化、反射产生新的实例。

线程不安全原因:

Java的乱序执行、初始化对象需要时间。
对于语句③,JVM在执行时大致做了下述三件事:
a. 在内存中分配一块内存
b. 调用构造方法
c. 将内存的地址指向instance变量。(执行这一步后,instance != null)

如果按照abc的顺序执行也不会有什么问题。但由于Java乱序执行的机制,有可能在真实情况下执行顺序为acb。
假设t1、t2是两个线程。t1执行到①时,发现为null,于是执行到语句③,先执行a,再执行c,在还没有执行b时,时间片给了t2。这时,由于instance已经分配了地址空间,instance不为null了。所以t2在执行到语句①后直接return instance,获得了这个还没有被初始化的对象,然后在使用时就报错了。

也就是说,在使用DCL时,有可能得到了实例的正确引用,但却访问到其成员变量的不正确值。

5. 双重检查锁DCL与volatile的联用

/**
* 双重检查锁DCL与volatile的联用
* @author 郑海鹏
* @since 2015年7月6日
*/
public class Singleton {
    private static volatile Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {

        if (null == instance) {
            synchronized (Singleton.class) {
                if (null == instance) {
                    instance = new Singleton(); // ③
                }
            }
        }
        return instance;
    }

}

优点:

线程安全、绝对单例

缺点:


  1. 代码看起来有点乱;
  2. 不能防止反序列化、反射产生新的实例
    线程安全的原因:

我纳闷这个很久,后来发现我只关注了volatitle的一个特性:对变量的读取每次都是从主存中读取。volatitle还有另外一个特性:volatile屏蔽指令重排序。也就是说,执行语句③时,肯定是按照abc的顺序执行的(参见四)。

6. 静态内部类 Initialization On Demand Holder

/**
* 静态内部类 Initialization On Demand Holder
* @author 郑海鹏
* @since 2015年7月6日
*/
public class Singleton {

    private static class SingletonHolder{
        static final Singleton instance = new Singleton();
    }

    private Singleton() {
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

}

优点:

线程安全,绝对单例

缺点:

不能防止反序列化、反射产生新的实例
和饿汉模式的区别:饿汉模式会在没有使用它的时候就已经加载实例。
线程安全的原因:Java的运行机制保证了static修饰的类、对象仅被初始化一次。

7. 枚举类

public enum Singleton {
    instance {
        String name = "this is name";

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    };

    public abstract void setName(String name);
    public abstract String getName();
}

优点:

线程安全、绝对单例、可以防止因为反序列化、反射产生新的实例

缺点:

和饿汉模式类似,会造成内存浪费。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值