博客
关于我
单例模式
阅读量:224 次
发布时间:2019-03-01

本文共 3886 字,大约阅读时间需要 12 分钟。

单例模式之饿汉与懒汉

在软件开发中,单例模式是一种常用的设计模式。它的核心思想是确保一个类在整个应用程序中只存在一个实例。这一模式在处理资源管理、线程安全等场景中表现尤为突出。本文将从饿汉式和懒汉式两种实现方式入手,分析它们的优缺点及其线程安全问题,并探讨如何解决这些问题。

饿汉式单例模式

饿汉式单例模式的特点是“早宴”,即在类加载时就创建单例实例。这种方式保证了线程安全,但可能会导致内存泄漏,因为单例实例在类加载时就已经创建,无法进行懒加载。

public class SingletonSimple1 {    private static final SingletonSimple1 instance = new SingletonSimple1();        private SingletonSimple1() {        // 私有化构造,不允许外部调用    }        public static SingletonSimple1 getInstance() {        return instance;    }}

**优点:** 线程安全。由于实例在类加载时已经创建,多线程环境下不会出现竞态条件。

**缺点:** 在不使用时占用内存,无法进行懒加载。这种方式会导致类加载时就占用内存资源,可能影响应用程序的性能。

懒汉式单例模式

懒汉式单例模式则采取“懒”态,即只有在第一次调用时才创建单例实例。这种方式在内存占用上更优,但同时也带来了线程安全问题,因为多个线程可能同时尝试创建单例实例,导致竞态条件。

public class SingletonSimple2 {    private static SingletonSimple2 instance;        private SingletonSimple2() {        // 私有化构造,不允许外部调用    }        public static SingletonSimple2 getInstance() {        if (null == instance) {            instance = new SingletonSimple2();        }        return instance;    }}

**优点:** 内存占用更优,适用于资源有限的场景。在不需要时不会占用内存,减少了内存浪费。

**缺点:** 线程安全问题。在多线程环境下,可能存在多个线程同时调用getInstance()方法,导致并发修改的情况。

解决懒汉式线程不安全的方案

方案一:加锁

为了解决懒汉式的线程不安全问题,一种常见的方法是在getInstance()方法中加锁。这样可以确保在多个线程之间不会发生竞争和数据竞态。

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

**优点:** 确保线程安全。通过加锁机制,保证在多线程环境下不会出现竞态条件。

**缺点:** 除了第一次创建实例之外,其余操作都是读操作,锁并未在后续操作中释放,导致资源占用的效率较低。

方案二:双重检查锁

为了进一步优化,可以在getInstance()方法中使用双重检查锁(double-checked locking)技术。这种方法在第一次获取实例时加锁,并确保在加锁后再次检查是否已经创建了实例。这样可以减少加锁的次数,提高效率。

public class SingletonSimple3 {    private static SingletonSimple3 instance;        private SingletonSimple3() {        // 私有化构造,不允许外部调用    }        public static SingletonSimple3 getInstance() {        if (null == instance) {            synchronized (SingletonSimple3.class) {                if (null == instance) {                    instance = new SingletonSimple3();                }            }        }        return instance;    }}

**优点:** 相比方案一,减少了加锁的次数,资源占用更高效。

**缺点:** 在极端情况下,可能会因为加锁时间过长而导致性能问题。此外,如果多个线程同时进入锁块,可能会导致并发修改的情况。

方案三:使用volatile关键字

为了进一步确保线程安全,可以在实例变量上使用volatile关键字。volatile会确保在多个线程之间的变量访问是可见的和一致的,避免了由于线程调度的原因导致的数据不一致问题。

public class SingletonSimple3 {    private static volatile SingletonSimple3 instance;        private SingletonSimple3() {        // 私有化构造,不允许外部调用    }        public static SingletonSimple3 getInstance() {        if (null == instance) {            synchronized (SingletonSimple3.class) {                if (null == instance) {                    instance = new SingletonSimple3();                }            }        }        return instance;    }}

**优点:** 使用volatile关键字可以避免由于线程调度导致的数据不一致问题,进一步增强线程安全性。

**缺点:** 这种方式仍然需要在getInstance()方法中加锁,虽然效率有所提升,但加锁本身仍然会影响性能。

方案四:使用ClassHolder

为了实现懒加载和线程安全,可以使用ClassHolder设计模式。这种方法通过在类内部定义一个静态的内部类(InstanceHolder),在第一次访问时初始化实例,并在之后直接返回实例。这种方法不仅支持懒加载,还能确保线程安全。

public class SingletonSimple5 {    private SingletonSimple5() {        // 私有化构造,不允许外部调用    }        private static class InstanceHolder {        private final static SingletonSimple5 instance = new SingletonSimple5();    }        public static SingletonSimple5 getInstance() {        return InstanceHolder.instance;    }}

**优点:** 支持懒加载,线程安全,且不会因为并发修改导致NPE(空指针异常)的问题。

方案五:使用枚举方式

除了上述方案,还有一种实现懒汉式单例模式的方式是通过枚举来实现单例的管理。这种方法虽然不常见,但仍然是一个有趣的思路。

枚举单例模式的实现方式如下所示:

public enum SingletonSimple4 {    INSTANCE;        private SingletonSimple4 instance;        SingletonSimple4() {        instance = new SingletonSimple4();    }        public static SingletonSimple4 getInstance() {        return SingletonSimple4.INSTANCE;    }}

**优点:** 使用枚举方式可以确保单例的唯一性,代码简洁明了。

**缺点:** 可能不如其他方案灵活,且在多个单例管理时会比较复杂。

总结

在选择单例模式的实现方式时,需要根据具体需求来权衡。饿汉式虽然线程安全,但内存占用较高;懒汉式则在内存占用上更优,但线程安全问题较为突出。通过对getInstance()方法中的锁机制的使用,可以有效解决懒汉式的线程安全问题,但需要权衡锁的开销对性能的影响。在实际开发中,可以根据具体场景选择适合的方案,同时结合ClassHolder、枚举方式等高级技巧,进一步优化单例模式的实现。

转载地址:http://obrv.baihongyu.com/

你可能感兴趣的文章
Objective-C实现wiggle sort摆动排序算法(附完整源码)
查看>>
Objective-C实现word frequency functions词频函数算法(附完整源码)
查看>>
Objective-C实现XOR Cipher异或密码算法(附完整源码)
查看>>
Objective-C实现XZordering算法(附完整源码)
查看>>
Objective-C实现y = sinx函数的积分运算(附完整源码)
查看>>
Objective-C实现y = x的平方函数的积分运算(附完整源码)
查看>>
Objective-C实现z-algorithm算法(附完整源码)
查看>>
Objective-C实现Zeller 的同余算法 (附完整源码)
查看>>
Objective-C实现zellers congruence泽勒一致算法(附完整源码)
查看>>
Objective-C实现Zero One Knapsack零一背包计算算法(附完整源码)
查看>>
Objective-C实现一个Pangram字符串至少包含一次所有字母算法(附完整源码)
查看>>
Objective-C实现一个stack算法(附完整源码)
查看>>
Objective-C实现一个通用的堆算法(附完整源码)
查看>>
Objective-C实现一分钟倒计时(附完整源码)
查看>>
Objective-C实现一阶高斯滤波(附完整源码)
查看>>
Objective-C实现万年历(附完整源码)
查看>>
Objective-C实现三次样条曲线(附完整源码)
查看>>
Objective-C实现三维空间点到直线的距离(附完整源码)
查看>>
Objective-C实现三维空间点到直线的距离(附完整源码)
查看>>
Objective-C实现三重缓冲区(附完整源码)
查看>>