单例模式(学习笔记)

  1. 意图

   保证一个类只有一个实例, 并提供一个访问该实例的全局节点java

  2. 动机

  • 控制某些共享资源(数据库或文件)的访问权限 
  • 为该实例提供一个全局访问点

  3. 适用性

  • 当类只能有一个实例并且客户能够从一个众所周知的访问点访问它时
  • 更严格的控制全局变量

  4. 结构

    

  5. 效果

  1) 对惟一实例的受控访问数据库

  2) 得到指向该实例的全局访问节点缓存

  3) 仅在首次请求单例对象时进行初始化安全

  4) 违反了单一职责原则。 该模式同时解决了两个问题(在一个方法中进行了建立类和提供类对象的操做)多线程

  5) 单例模式可能掩盖不良设计, 好比程序各组件之间相互了解过多等app

  6) 该模式在多线程环境下须要进行特殊处理, 避免多个线程屡次建立单例对象框架

  7) 单例的客户端代码单元测试可能会比较困难, 由于许多测试框架以基于继承的方式建立模拟对象。 因为单例类的构造函数是私有的, 并且绝大部分语言没法重写静态方法, 因此你须要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码, 或者不使用单例模式less

  6. 代码实现

  Singleton_singlethread.java: 单例(单线程)ide

package singleton;

/**
 * @author GaoMing
 * @date 2021/7/18 - 19:13
 */
public class Singleton_singlethread {
    private static Singleton_singlethread instance;
    public String value;

    private Singleton_singlethread(String value) {
        // The following code emulates slow initialization.
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        this.value = value;
    }

    public static Singleton_singlethread getInstance(String value) {
        if (instance == null) {
            instance = new Singleton_singlethread(value);
        }
        return instance;
    }
}

  DemoSingleThread.java: 客户端代码函数

package singleton;

/**
 * @author GaoMing
 * @date 2021/7/18 - 19:16
 */
public class DemoSingleThread {
    public static void main(String[] args) {
        System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
                "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
                "RESULT:" + "\n");
        Singleton_singlethread singleton = Singleton_singlethread.getInstance("FOO");
        Singleton_singlethread anotherSingleton = Singleton_singlethread.getInstance("BAR");
        System.out.println(singleton.value);
        System.out.println(anotherSingleton.value);
    }
}

  执行结果

If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)

RESULT:

FOO
FOO

  Singleton_multithread.java: 单例(多线程安全单例)

package singleton;

/**
 * @author GaoMing
 * @date 2021/7/18 - 19:12
 */
public final class Singleton_multithread {
    private static volatile Singleton_multithread instance;

    public String value;

    private Singleton_multithread(String value) {
        this.value = value;
    }

    public static Singleton_multithread getInstance(String value) {
        // The approach taken here is called double-checked locking (DCL). It
        // exists to prevent race condition between multiple threads that may
        // attempt to get singleton instance at the same time, creating separate
        // instances as a result.
        //
        // It may seem that having the `result` variable here is completely
        // pointless. There is, however, a very important caveat when
        // implementing double-checked locking in Java, which is solved by
        // introducing this local variable.
        //
        // You can read more info DCL issues in Java here:
        // https://refactoring.guru/java-dcl-issue
        Singleton_multithread result = instance;
        if (result != null) {
            return result;
        }
        synchronized(Singleton_multithread.class) {
            if (instance == null) {
                instance = new Singleton_multithread(value);
            }
            return instance;
        }
    }
}

  DemoMultiThread.java: 客户端代码

package singleton;

/**
 * @author GaoMing
 * @date 2021/7/18 - 19:30
 */
public class DemoMultiThread {
    public static void main(String[] args) {
        System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
                "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
                "RESULT:" + "\n");
        Thread threadFoo = new Thread(new ThreadFoo());
        Thread threadBar = new Thread(new ThreadBar());
        threadFoo.start();
        threadBar.start();
    }

    static class ThreadFoo implements Runnable {
        @Override
        public void run() {
            Singleton_multithread singleton = Singleton_multithread.getInstance("FOO");
            System.out.println(singleton.value);
        }
    }

    static class ThreadBar implements Runnable {
        @Override
        public void run() {
            Singleton_multithread singleton = Singleton_multithread.getInstance("BAR");
            System.out.println(singleton.value);
        }
    }
}

  执行结果

If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)

RESULT:

BAR
BAR

 

  7. 与其余模式的关系

  •  外观模式类一般能够转换为单例模式类, 由于在大部分状况下一个外观对象就足够了
  • 抽象工厂模式、 生成器模式和原型模式均可以用单例来实现

  8. 已知应用 

  • java.lang.Runtime#getRuntime()
  • java.awt.Desktop#getDesktop()
  • java.lang.System#getSecurityManager()

  识别方法: 单例能够经过返回相同缓存对象的静态构建方法来识别。

相关文章
相关标签/搜索