一个类中只有一个实例, 且能够自行实例化提供这个实例, 同时提供全局访问的方法.

结构

1.构造私有化: 确保外部不能使用new直接创建对象
2.内部静态属性创建实例
3.对外公共静态获取对象方法

demo

  1. /**
  2. * 单例模式
  3. * 1. 构造私有化: 确保外部不能使用new直接创建对象
  4. * 2. 内部静态属性创建实例
  5. * 3. 对外公共静态获取对象方法
  6. *
  7. * @author liuzhihang
  8. * @date 2018/3/27 17:45
  9. */
  10. public class SingletonPattern {
  11. private SingletonPattern() {
  12. }
  13. private static SingletonPattern singletonPattern = null;
  14. public static SingletonPattern getSingletonPattern() {
  15. if (singletonPattern == null) {
  16. singletonPattern = new SingletonPattern();
  17. }
  18. return singletonPattern;
  19. }
  20. }

分类

1.懒汉式: 懒汉模式, 项目启动时不生成对象, 而是在首次创建该对象的时候生成唯一实例

  1. /**
  2. * 懒汉模式, 项目启动时不生成对象, 而是在首次创建该对象的时候生成唯一实例
  3. *
  4. * @author liuzhihang
  5. * @date 2018/4/2 16:24
  6. */
  7. public class LazyPattern {
  8. private LazyPattern() {
  9. }
  10. private static LazyPattern lazyPattern = null;
  11. public static LazyPattern getLazyPattern() {
  12. try {
  13. if (lazyPattern == null) {
  14. // 模拟一系列耗时操作
  15. Thread.sleep(50);
  16. lazyPattern = new LazyPattern();
  17. }
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. return lazyPattern;
  22. }
  23. }

2.饿汉式: 项目启动时, 进行加载, 会导致项目启动较慢, 并且无论后面是否用到都会进行加载

  1. /**
  2. *
  3. * 饿汉式单例模式
  4. * 项目启动时, 进行加载, 会导致项目启动较慢, 并且无论后面是否用到都会进行加载
  5. *
  6. * @author liuzhihang
  7. * @date 2018/4/2 18:44
  8. */
  9. public class HungerPattern {
  10. private HungerPattern() {
  11. }
  12. private static HungerPattern hungerPattern = new HungerPattern();
  13. public static HungerPattern getHungerPattern() {
  14. return hungerPattern;
  15. }
  16. }

测试用例

在多线程情况下对单例模式进行测试:

  1. /**
  2. * @author liuzhihang
  3. * @date 2018/3/27 19:02
  4. */
  5. public class SingletonTest {
  6. public static void main(String[] args) {
  7. ThreadTest[] threadTests = new ThreadTest[10];
  8. for (int i = 0; i < threadTests.length; i++) {
  9. threadTests[i] = new ThreadTest();
  10. }
  11. for (int i = 0; i < threadTests.length; i++) {
  12. threadTests[i].start();
  13. }
  14. }
  15. }
  16. class ThreadTest extends Thread {
  17. @Override
  18. public void run() {
  19. // 懒汉模式
  20. System.out.println(LazyPattern.getLazyPattern().hashCode());
  21. // 饿汉模式
  22. // System.out.println(HungerPattern.getHungerPattern().hashCode());
  23. }
  24. }

结果:
1.饿汉模式

  1. D:\jdk1.8\bin\java.exe . . .
  2. 1294123621
  3. 1294123621
  4. 1294123621
  5. 1294123621
  6. 1294123621
  7. 1294123621
  8. 1294123621
  9. 1294123621
  10. 1294123621
  11. 1294123621
  12. Process finished with exit code 0

2.懒汉模式

  1. D:\jdk1.8\bin\java.exe . . .
  2. 140919816
  3. 1359128134
  4. 1385166630
  5. 924507082
  6. 67641385
  7. 508832262
  8. 574926395
  9. 140919816
  10. 1442414714
  11. 896298396
  12. Process finished with exit code 0

结论: 在懒汉单例模式下不能保证线程的安全性

懒汉模式的线程安全优化

饿汉模式会造成资源浪费, 启动慢等结果, 下面对懒汉模式进行线程安全优化.

synchronized 锁住静态方法

锁住静态方法 类级锁 影响范围较大, 导致效率相对较低

  1. /**
  2. * 懒汉式
  3. * 在方法上添加 synchronized 关键字 锁类
  4. * 同步方法的方式, 导致效率相对较低
  5. *
  6. * @author liuzhihang
  7. * @date 2018/4/3 14:27
  8. */
  9. public class SyncLazyPattern {
  10. private SyncLazyPattern() {
  11. }
  12. private static SyncLazyPattern syncLazyPattern = null;
  13. public static synchronized SyncLazyPattern getSyncLazyPattern() {
  14. try {
  15. if (syncLazyPattern == null) {
  16. Thread.sleep(100);
  17. syncLazyPattern = new SyncLazyPattern();
  18. }
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. return syncLazyPattern;
  23. }
  24. }

synchronized 锁住代码块

  1. package com.liuzhihang.demo.singleton;
  2. /**
  3. * 锁代码块的方式虽然可以保证结果一致性
  4. * 但锁住很多操作, 同样会导致效率低下
  5. *
  6. * @author liuzhihang
  7. * @date 2018/4/3 15:22
  8. */
  9. public class SyncCodeBlockLazyPattern {
  10. private SyncCodeBlockLazyPattern() {
  11. }
  12. private static SyncCodeBlockLazyPattern syncCodeBlockLazyPattern = null;
  13. public static SyncCodeBlockLazyPattern getSyncCodeBlockLazyPattern() {
  14. try {
  15. // 锁住具体执行业务逻辑的代码
  16. synchronized (SyncCodeBlockLazyPattern.class) {
  17. if (syncCodeBlockLazyPattern == null) {
  18. Thread.sleep(100);
  19. syncCodeBlockLazyPattern = new SyncCodeBlockLazyPattern();
  20. }
  21. }
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. return syncCodeBlockLazyPattern;
  26. }
  27. }

双重检查锁机制(推荐)

  1. package com.liuzhihang.demo.singleton;
  2. /**
  3. * 双重锁检查机制, 仅锁住创建对象的部分代码
  4. * 注意: 在对象前 添加 volatile 关键字 确保可见性, 即 每次获取值从主内存中获取, 同时防止指令重排序
  5. *
  6. * @author liuzhihang
  7. * @date 2018/4/3 15:29
  8. */
  9. public class DubboCheckLockLazyPattern {
  10. private DubboCheckLockLazyPattern() {
  11. }
  12. private static volatile DubboCheckLockLazyPattern dubboCheckLockLazyPattern = null;
  13. public static DubboCheckLockLazyPattern getDubboCheckLockLazyPattern() {
  14. try {
  15. if (dubboCheckLockLazyPattern == null) {
  16. // 一系列操作
  17. Thread.sleep(100);
  18. synchronized (DubboCheckLockLazyPattern.class) {
  19. // 二次检查
  20. if (dubboCheckLockLazyPattern == null) {
  21. dubboCheckLockLazyPattern = new DubboCheckLockLazyPattern();
  22. }
  23. }
  24. }
  25. } catch (InterruptedException e) {
  26. e.printStackTrace();
  27. }
  28. return dubboCheckLockLazyPattern;
  29. }
  30. }