单例模式

单例模式8种创建方式

  1. 饿汉式(静态常量)
  2. 饿汉式(静态代码块)
  3. 懒汉式(线程不安全)
  4. 懒汉式(线程安全 同步方法)
  5. 懒汉式(线程安全 同步代码块)
  6. 双重检查(推荐使用)
  7. 静态内部类(推荐使用)
  8. 枚举(推荐使用)

1.饿汉式(静态常量)

创建步骤
  1. 构造器私有化(防止new)
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法
  4. 代码实现
public class StaticVariableSingletonTest {

    /**
     * @param
     * @return void
     * @Description: 测试--饿汉式--静态变量
     * @author luoyong
     * @create 21:33 2019/9/7
     * @last modify by [LuoYong 21:33 2019/9/7 ]
     */
    @Test
    public void test() {
        StaticVariableSingleton singletonOne = StaticVariableSingleton.getInstance();
        StaticVariableSingleton singletonOTwo = StaticVariableSingleton.getInstance();
        //内存地址相同
        System.out.println(singletonOne == singletonOTwo);
        System.out.println("singletonOne.hashCode=" + singletonOne.hashCode());
        System.out.println("singletonOTwo.hashCode=" + singletonOTwo.hashCode());
    }
}


class StaticVariableSingleton {

    /**
     * 1.构造器私有化, 外部不能new
     */
    private StaticVariableSingleton() {

    }

    /**
     * 2.本类内部创建对象实例
     */
    private final static StaticVariableSingleton instance = new StaticVariableSingleton();

    /**
     * @param
     * @return
     * @Description: 3 提供一个公有的静态方法,返回实例对象
     * @author luoyong
     * @create 21:33 2019/9/7
     * @last modify by [LuoYong 21:33 2019/9/7 ]
     */
    public static StaticVariableSingleton getInstance() {
        return instance;
    }
优点

写 法 比 较 简 单 , 在 类 装 载 的 时 候 完 成 实 例 化 , 避 免 了 线 程 同 步 的 问 题 \color{red}{写法比较简单,在类装载的时候完成实例化,避免了线程同步的问题} 线

缺点

在 类 装 载 的 时 候 就 完 成 了 实 例 化 , 没 有 达 到 L a z y L o a d i n g 的 效 果 。 如 果 类 没 有 被 使 用 过 , 会 造 成 内 存 的 浪 费 \color{green}{在类装载的时候就完成了实例化,没有达到Lazy Loading的效果。如果类没有被使用过,会造成内存的浪费} ,LazyLoading使

结论

这 种 单 例 模 式 可 用 , 可 能 操 作 内 存 的 浪 费 。 ( 如 果 确 定 这 个 类 一 定 会 使 用 到 可 以 这 样 写 ) \color{blue}{这种单例模式可用,可能操作内存的浪费。(如果确定这个类一定会使用到 可以这样写)} 使

2.饿汉式(静态代码块)

创建步骤
  1. 构造器私有化(防止new)
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法
  4. 代码实现
public class StaticBlockSingletonTest {

    /**
     * @param
     * @return void
     * @Description: 饿汉式--静态代码块--测试
     * @author luoyong
     * @create 21:51 2019/9/7
     * @last modify by [LuoYong 21:51 2019/9/7 ]
     */
    @Test
    public void test() {
        Singleton singletonOne = Singleton.getInstance();
        Singleton singletonOTwo = Singleton.getInstance();
        //内存地址相同
        System.out.println(singletonOne == singletonOTwo);
        System.out.println("singletonOne.hashCode=" + singletonOne.hashCode());
        System.out.println("singletonOTwo.hashCode=" + singletonOTwo.hashCode());
    }
}


/**
 * 饿汉式(静态变量)
 */
class Singleton {

    /**
     * 1.构造器私有化, 外部能new
     */
    private Singleton() {
    }
    
    /**
     * 2.本类内部创建对象实例
     */
    private static Singleton instance;

    static {
        // 在静态代码块中,创建单例对象
        instance = new Singleton();
    }

    /**
     * @param
     * @return
     * @Description: 3.提供一个公有的静态方法,返回实例对象
     * @author luoyong
     * @create 21:51 2019/9/7
     * @last modify by [LuoYong 21:51 2019/9/7 ]
     */
    public static Singleton getInstance() {
        return instance;
    }
}
优点缺点–>同饿汉式静态常量

3.懒汉式(线程不安全)

实现
/**
 * @author luoyong
 * @Description: 懒汉式--线程不安全
 * 起到了Lazy Loading的效果 但是只能在单线程下使用
 * 在多线程的环境下可能产生多个实例
 * 结论:在实际开发当中,不要使用这种方式
 * @create 2019-09-07 22:00
 * @last modify by [LuoYong 2019-09-07 22:00]
 **/
public class ThreadUnsafeSingletonTest {

    /**
     * @param
     * @return void
     * @Description: 懒汉式--线程不安全--测试
     * @author luoyong
     * @create 22:02 2019/9/7
     * @last modify by [LuoYong 22:02 2019/9/7 ]
     */
    @Test
    public void test() {
        System.out.println("懒汉式--线程不安全");
        Singleton singletonOne = Singleton.getInstance();
        Singleton singletonOTwo = Singleton.getInstance();
        //内存地址相同
        System.out.println(singletonOne == singletonOTwo);
        System.out.println("singletonOne.hashCode=" + singletonOne.hashCode());
        System.out.println("singletonOTwo.hashCode=" + singletonOTwo.hashCode());
    }
}

class Singleton {

    /**
     * 1.构造器私有化, 外部能new
     */
    private Singleton() {

    }


    /**
     * 2.本类内部创建对象实例
     */
    private static Singleton instance;

    /**
     * @param
     * @return
     * @Description: 3.提供一个公有的静态方法,返回实例对象
     * 调用的时候创建
     * @author luoyong
     * @create 21:51 2019/9/7
     * @last modify by [LuoYong 21:51 2019/9/7 ]
     */
    public static Singleton getInstance() {
        if (null == instance) {
            instance = new Singleton();
        }
        return instance;
    }
优点

起 到 了 L a z y − L o a d i n g 的 效 果 , 但 是 只 能 在 单 线 程 下 使 用 \color{red}{起到了Lazy-Loading的效果,但是只能在单线程下使用} LazyLoading线使

缺点

在 多 线 程 下 , 一 个 线 程 进 入 了 i f ( n u l l = = i n s t a n c e ) 判 断 语 句 块 , 还 未 来 得 及 往 下 执 行 \color{green}{在多线程下,一个线程进入了if (null == instance)判断语句块,还未来得及往下执行} 线线if(null==instance)
另 一 个 线 程 也 进 入 了 这 个 判 断 语 句 块 , 这 时 便 会 产 生 多 个 实 例 \color{green}{另一个线程也进入了这个判断语句块,这时便会产生多个实例} 线便
所 以 在 多 线 程 环 境 下 不 可 使 用 这 个 方 式 \color{green}{所以在多线程环境下不可使用这个方式} 线使

结论

在 实 际 开 发 当 中 , 不 要 使 用 这 种 方 式 \color{blue}{在实际开发当中,不要使用这种方式} 使

4-5.懒汉式–线程安全(同步代码块、同步方法)

实现
/**
 * @author luoyong
 * @Description: 懒汉式--线程安全
 * 解决了线程安全的问题
 * 缺点--效率太低,每个线程想获取该类的实例的时候 getInstance都要同步。
 * 结论:在实际开发当中 不推荐使用这种方式
 * @create 2019-09-07 22:08
 * @last modify by [LuoYong 2019-09-07 22:08]
 **/
public class ThreadSafeSingletonTest {

    /**
     * @param
     * @return void
     * @Description: 懒汉式--线程安全--加入同步处理代码--测试
     * @author luoyong
     * @create 22:02 2019/9/7
     * @last modify by [LuoYong 22:02 2019/9/7 ]
     */
    @Test
    public void test() {
        System.out.println("懒汉式--线程安全");
        SingletonSafe singletonOne = SingletonSafe.getInstance();
        SingletonSafe singletonOTwo = SingletonSafe.getInstance();
        //内存地址相同
        System.out.println(singletonOne == singletonOTwo);
        System.out.println("singletonOne.hashCode=" + singletonOne.hashCode());
        System.out.println("singletonOTwo.hashCode=" + singletonOTwo.hashCode());
    }
}


class SingletonSafe {

    /**
     * 构造器私有化, 外部能new
     */
    private SingletonSafe() {

    }


    /**
     * 本类内部创建对象实例
     */
    private static SingletonSafe instance;

    /**
     * @param
     * @return
     * @Description: 提供一个公有的静态方法,返回实例对象
     * 调用的时候创建
     * 加入同步处理的代码--解决线程安全问题
     * @author luoyong
     * @create 21:51 2019/9/7
     * @last modify by [LuoYong 21:51 2019/9/7 ]
     */
    public static synchronized SingletonSafe getInstance() {
        if (null == instance) {
            instance = new SingletonSafe();
        }
        return instance;
    }

    /**
     * @param
     * @return
     * @Description: 这种方本意是对第四种方式进行改进,改成同步产生实例的化的代码块
     * 缺点:这种方式不能保证线程同步。也可能产生多个实例
     * 结论:在实际开发当中,不能使用这种方式
     * @author luoyong
     * @create 22:21 2019/9/7
     * @last modify by [LuoYong 22:21 2019/9/7 ]
     */
    public static SingletonSafe getInstanceUnSafe() {
        if (null == instance) {
            synchronized (SingletonSafe.class) {
                instance = new SingletonSafe();
            }
        }
        return instance;
    }

同步方法优点

解 决 了 线 程 不 安 全 的 问 题 \color{red}{解决了线程不安全的问题} 线

同步方法缺点

效 率 太 低 , 每 个 线 程 在 想 获 取 类 的 实 例 的 时 候 , 执 行 g e t I n s t a n c e ( ) 方 法 都 要 进 行 同 步 \color{green}{效率太低,每个线程在想获取类的实例的时候,执行getInstance()方法都要进行同步} 线getInstance()
而 其 实 这 个 方 法 只 执 行 一 次 实 例 化 代 码 就 就 够 了 , 后 面 想 要 获 取 该 实 例 , 直 接 r e t u r n 就 可 以 了 , 方 法 进 行 同 步 效 率 太 低 \color{green}{而其实这个方法只执行一次实例化代码就就够了,后面想要获取该实例,直接return就可以了,方法进行同步效率太低} return

同步方法结论

在 实 际 开 发 中 , 不 推 荐 使 用 这 种 方 式 \color{blue}{在实际开发中,不推荐使用这种方式} 使

同步代码块优点

第 四 种 方 式 的 改 进 , 同 步 效 率 太 低 \color{red}{第四种方式的改进,同步效率太低}

同步代码块缺点

这 种 同 步 并 不 能 起 到 线 程 同 步 作 用 。 跟 第 三 种 实 现 方 式 遇 到 的 情 形 一 致 \color{green}{这种同步并不能起到线程同步作用。跟第三种实现方式遇到的情形一致} 线
假 如 一 个 线 程 进 入 了 i f ( s i n g l e t o n = = n u l l ) 判 断 语 句 块 , 还 未 来 得 及 往 下 执 行 , 另 一 个 线 程 也 通 过 了 这 个 判 断 语 句 , 这 时 便 会 产 生 多 个 实 例 \color{green}{假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行, 另一个线程也通过了这个判断语句,这时便会产生多个实例} 线if(singleton==null)线便

同步代码块结论

在 实 际 开 发 中 , 不 能 使 用 这 种 方 式 \color{blue}{在实际开发中,不能使用这种方式} 使

6.双重检查

实现
/**
 * @author luoyong
 * @Description: 懒汉式--双重检查
 * 在多线程开发当中经常使用到
 * 优点:线程安全 延迟加载 效率较高
 * 结论:在实际开发当中,推荐使用这种单例设计模式
 * @create 2019-09-07 22:28
 * @last modify by [LuoYong 2019-09-07 22:28]
 **/
public class DoubleCheckSingletonTest {

    /**
     * @param
     * @return void
     * @Description: 测试双重检查实现单例
     * @author luoyong
     * @create 22:35 2019/9/7
     * @last modify by [LuoYong 22:35 2019/9/7 ]
     */
    @Test
    public void test() {
        System.out.println("双重检查--线程安全和懒加载");
        Singleton singletonOne = Singleton.getInstance();
        Singleton singletonOTwo = Singleton.getInstance();
        //内存地址相同
        System.out.println(singletonOne == singletonOTwo);
        System.out.println("singletonOne.hashCode=" + singletonOne.hashCode());
        System.out.println("singletonOTwo.hashCode=" + singletonOTwo.hashCode());
    }
}


class Singleton {

    private Singleton() {
    }

    /**
     * 本类内部创建对象实例
     * volatile
     * 1:保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的(实现可见性)
     * 2:禁止进行指令重排序。(实现有序性)
     * 3:只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性
     */
    private static volatile Singleton singleton;

    /**
     * @param
     * @return
     * @Description: 双重检查 既达到了线程安全,又实现了懒加载
     * @author luoyong
     * @create 22:21 2019/9/7
     * @last modify by [LuoYong 22:21 2019/9/7 ]
     */
    public static Singleton getInstance() {
        if (null == singleton) {
            synchronized (Singleton.class) {
                if (null == singleton) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
优点

线 程 安 全 延 迟 加 载 效 率 较 高 \color{red}{线程安全 延迟加载 效率较高} 线

结论

在 实 际 开 发 过 程 中 , 推 荐 使 用 这 种 单 例 设 计 模 式 \color{blue}{在实际开发过程中,推荐使用这种单例设计模式} 使

7.静态内部类

实现
/**
 * @author luoyong
 * @Description: 静态内部类--创建单例
 * @create 2019-09-07 23:09
 * @last modify by [LuoYong 2019-09-07 23:09]
 **/
public class StaticInnerClassesSingletonTest {
    /**
     * @param
     * @return void
     * @Description: 静态内部类--创建单例--推荐
     * @author luoyong
     * @create 23:22 2019/9/7
     * @last modify by [LuoYong 23:22 2019/9/7 ]
     */
    @Test
    public void test() {
        System.out.println("静态内部类--线程安全和懒加载");
        StaticInnerSingleton singletonOne = StaticInnerSingleton.getInstance();
        StaticInnerSingleton singletonOTwo = StaticInnerSingleton.getInstance();
        //内存地址相同
        System.out.println(singletonOne == singletonOTwo);
        System.out.println("singletonOne.hashCode=" + singletonOne.hashCode());
        System.out.println("singletonOTwo.hashCode=" + singletonOTwo.hashCode());
    }
}

class StaticInnerSingleton {

    private StaticInnerSingleton() {
    }


    /**
     * 静态内部类
     * 1:StaticInnerSingleton 在装载的时候不会装载StaticInnerSingletonInit
     * 2:jvm在装载类的时候是线程安全的
     * 类的静态属性只会在第一次加载的时候初始化
     * 推荐使用
     */
    private static class StaticInnerSingletonInit {
        private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
    }

    public static StaticInnerSingleton getInstance() {
        return StaticInnerSingletonInit.INSTANCE;
    }
}
优缺点说明

1. 这 种 方 式 采 用 了 类 装 载 的 机 制 来 保 证 初 始 化 实 例 时 只 有 一 个 线 程 \color{blue}{1.这种方式采用了类装载的机制来保证初始化实例时只有一个线程} 1.线
2. 静 态 内 部 类 方 式 在 S t a t i c I n n e r S i n g l e t o n 类 被 装 载 时 并 不 会 立 即 实 例 化 。 是 在 需 要 实 例 化 时 , 调 用 g e t I n s t a n c e 方 法 , 才 会 装 载 S t a t i c I n n e r S i n g l e t o n I n i t 类 , 从 而 完 成 S t a t i c I n n e r S i n g l e t o n 的 实 例 化 。 \color{blue}{2.静态内部类方式在StaticInnerSingleton类被装载时并不会立即实例化。是在需要实例化 时,调用getInstance方法,才会装载StaticInnerSingletonInit类,从而完成StaticInnerSingleton的 实例化。} 2.StaticInnerSingletongetInstanceStaticInnerSingletonInitStaticInnerSingleton
3. 类 的 静 态 属 性 只 会 在 第 一 次 加 载 类 的 时 候 初 始 化 , 所 以 在 这 里 , J V M 帮 助 我 们 保 证 了 线 程 的 安 全 性 , 在 类 进 行 初 始 化 时 , 别 的 线 程 是 无 法 进 入 的 。 \color{blue}{3.类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们 保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。} 3.JVM线线
优 点 : 避 免 了 线 程 不 安 全 , 利 用 静 态 内 部 类 特 点 实 现 延 迟 加 载 , 效 率 高 \color{red}{优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高} 线
结 论 : 推 荐 使 用 \color{red}{结论:推荐使用} :使

8.枚举

实现
/**
 * @author luoyong
 * @Description: 枚举--创建单例
 * 借助JDK 1.5当中添加的枚举来实现单例模式
 * 1:批量了多线程同步问题
 * 2:防止反序列化重新创建新的对象
 * 结论:推荐使用
 * @create 2019-09-07 23:20
 * @last modify by [LuoYong 2019-09-07 23:20]
 **/
public class EnumSingletonTest {
    @Test
    public void test() {
        System.out.println("枚举--线程安全和懒加载");
        EnumSingleton singletonOne = EnumSingleton.INSTANCE;
        EnumSingleton singletonOTwo = EnumSingleton.INSTANCE;
        //内存地址相同
        System.out.println(singletonOne == singletonOTwo);
        System.out.println("singletonOne.hashCode=" + singletonOne.hashCode());
        System.out.println("singletonOTwo.hashCode=" + singletonOTwo.hashCode());
    }
}


enum EnumSingleton {

    INSTANCE;

    private void show() {
        System.out.println("ok");
    }
}
优缺点说明

1. 这 借 助 J D K 1.5 中 添 加 的 枚 举 来 实 现 单 例 模 式 。 不 仅 能 避 免 多 线 程 同 步 问 题 , 而 且 还 能 防 止 反 序 列 化 重 新 创 建 新 的 对 象 \color{blue}{1.这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而 且还能防止反序列化重新创建新的对象} 1.JDK1.5线
这 种 方 式 是 E f f e c t i v e J a v a 作 者 J o s h B l o c h 提 倡 的 方 式 \color{blue}{这种方式是Effective Java作者Josh Bloch 提倡的方式} EffectiveJavaJoshBloch
结 论 : 推 荐 使 用 \color{red}{结论:推荐使用} :使

源码使用举例

单例模式在JDK 应用的源码分析

在这里插入图片描述

单例模式注意事项和细节说明

注意事项

1. 单 例 模 式 保 证 了 系 统 内 存 中 该 类 只 存 在 一 个 对 象 , 节 省 了 系 统 资 源 , 对 于 一 些 需 要 频 繁 创 建 销 毁 的 对 象 , 使 用 单 例 模 式 可 以 提 高 系 统 性 能 \color{red}{1.单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需 要频繁创建销毁的对象,使用单例模式可以提高系统性能} 1.使
2. 当 想 实 例 化 一 个 单 例 类 的 时 候 , 必 须 要 记 住 使 用 相 应 的 获 取 对 象 的 方 法 , 而 不 是 使 用 n e w \color{red}{2.当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使 用new} 2.使使new

使用场景

需 要 频 繁 的 进 行 创 建 和 销 毁 的 对 象 、 创 建 对 象 时 耗 时 过 多 或 耗 费 资 源 过 多 ( 即 : 重 量 级 对 象 ) , 但 又 经 常 用 到 的 对 象 、 工 具 类 对 象 、 频 繁 访 问 数 据 库 或 文 件 的 对 象 ( 比 如 数 据 源 、 s e s s i o n 工 厂 等 ) \color{blue}{需要频繁的进行创建和销毁的对象、创建对象时耗时过多或 耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数 据库或文件的对象(比如数据源、session工厂等)} (:)访(session)

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值