2.1 题目

写一个 Singleton 示例

2.2 什么是 Singleton

  • Singleton:在 Java 中即指单例设计模式,它是软件开发中最常用的设计模式之一
  • 单:唯一
  • 例:实例
  • 单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式
  • 例如:代表 JVM 运行环境的 Runtime 类

    2.3 要点

  • 某个类只能有一个实例

  • 它必须自行创建这个实例
    • 含有一个该类的静态变量来保存这个唯一的实例
  • 它必须自行向整个系统提供这个实例

    • 对外提供获取该实例对象的方式
      • 直接暴露
      • 用静态变量的 get 方法获取

        2.4 几种常见形式

  • 饿汉式:直接创建对象,不存在线程安全问题

    • 直接实例化饿汉式(简洁直观)
    • 枚举式(最简洁)
    • 静态代码块饿汉式(适合复杂实例化)
  • 懒汉式:延迟创建对象

    • 线程不安全(适用于单线程)
    • 线程安全(适用于多线程)
    • 静态内部类形式(适用于多线程)

      2.5 代码示例

      饿汉式:在类初始化时直接创建实例对象,不管你是否需要这个对象都会创建
  • 构造器私有化

  • 自行创建,并且用静态变量保存
  • 向外提供这个实例
  • 强调这是一个单例,我们可以用 final 修改 ```java package s01.e02;

public class Singleton1 { public static final Singleton1 INSTANCE = new Singleton1();

  1. private Singleton1() {
  2. }

}

  1. 枚举类型:表示该类型的对象是有限的几个,我们可以限定一个,就成了单例
  2. ```java
  3. package s01.e02;
  4. public enum Singleton2 {
  5. INSTANCE
  6. }

调用方法

  1. package s01.e02;
  2. public class TestSingleton1 {
  3. public static void main(String[] args) {
  4. Singleton1 s = Singleton1.INSTANCE;
  5. System.out.println(s);
  6. }
  7. }

image.png

  1. package s01.e02;
  2. public class TestSingleton2 {
  3. public static void main(String[] args) {
  4. Singleton2 s = Singleton2.INSTANCE;
  5. System.out.println(s);
  6. }
  7. }

image.png
静态代码块饿汉式

  1. package s01.e02;
  2. import java.io.IOException;
  3. import java.util.Properties;
  4. public class Singleton3 {
  5. public static final Singleton3 INSTANCE;
  6. private String info;
  7. static {
  8. try {
  9. Properties pro = new Properties();
  10. pro.load(Singleton3.class.getClassLoader().getResourceAsStream("single.properties"));
  11. INSTANCE = new Singleton3(pro.getProperty("info"));
  12. } catch (IOException e) {
  13. throw new RuntimeException(e);
  14. }
  15. }
  16. private Singleton3(String info) {
  17. this.info = info;
  18. }
  19. public String getInfo() {
  20. return info;
  21. }
  22. public void setInfo(String info) {
  23. this.info = info;
  24. }
  25. @Override
  26. public String toString() {
  27. return "Singleton3{" +
  28. "info='" + info + '\'' +
  29. '}';
  30. }
  31. }
  1. package s01.e02;
  2. public class TestSingleton3 {
  3. public static void main(String[] args) {
  4. Singleton3 s = Singleton3.INSTANCE;
  5. System.out.println(s);
  6. }
  7. }
  1. info=atguigu

image.png
懒汉式:延迟创建这个实例对象

  • 构造器私有化
  • 用一个静态变量保存这个唯一的实例
  • 提供一个静态方法,获取这个实例对象

单线程安全演示

  1. package s01.e02;
  2. public class Singleton4 {
  3. private static Singleton4 instance;
  4. private Singleton4() {
  5. }
  6. public static Singleton4 getInstance() {
  7. if (instance == null) {
  8. instance = new Singleton4();
  9. }
  10. return instance;
  11. }
  12. }
  1. package s01.e02;
  2. public class TestSingleton4 {
  3. public static void main(String[] args) {
  4. Singleton4 s1 = Singleton4.getInstance();
  5. Singleton4 s2 = Singleton4.getInstance();
  6. System.out.println(s1 == s2);
  7. System.out.println(s1);
  8. System.out.println(s2);
  9. }
  10. }

image.png
线程不安全演示

  1. package s01.e02;
  2. public class Singleton4 {
  3. private static Singleton4 instance;
  4. private Singleton4() {
  5. }
  6. public static Singleton4 getInstance() {
  7. if (instance == null) {
  8. try {
  9. Thread.sleep(100);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. instance = new Singleton4();
  14. }
  15. return instance;
  16. }
  17. }
  1. package s01.e02;
  2. import java.util.concurrent.*;
  3. public class TestSingleton4 {
  4. public static void main(String[] args) throws ExecutionException, InterruptedException {
  5. Callable<Singleton4> c = Singleton4::getInstance;
  6. ExecutorService es = Executors.newFixedThreadPool(2);
  7. Future<Singleton4> f1 = es.submit(c);
  8. Future<Singleton4> f2 = es.submit(c);
  9. Singleton4 s1 = f1.get();
  10. Singleton4 s2 = f2.get();
  11. System.out.println(s1 == s2);
  12. System.out.println(s1);
  13. System.out.println(s2);
  14. es.shutdown();
  15. }
  16. }

image.png
线程安全演示

  1. package s01.e02;
  2. public class Singleton5 {
  3. private static Singleton5 instance;
  4. private Singleton5() {
  5. }
  6. public static Singleton5 getInstance() {
  7. if (instance == null) { // 优化性能,不为空时不需要锁住等待
  8. synchronized (Singleton5.class) {
  9. if (instance == null) {
  10. try {
  11. Thread.sleep(1000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. instance = new Singleton5();
  16. }
  17. }
  18. }
  19. return instance;
  20. }
  21. }
  1. package s01.e02;
  2. import java.util.concurrent.*;
  3. public class TestSingleton5 {
  4. public static void main(String[] args) throws ExecutionException, InterruptedException {
  5. Callable<Singleton5> c = Singleton5::getInstance;
  6. ExecutorService es = Executors.newFixedThreadPool(2);
  7. Future<Singleton5> f1 = es.submit(c);
  8. Future<Singleton5> f2 = es.submit(c);
  9. Singleton5 s1 = f1.get();
  10. Singleton5 s2 = f2.get();
  11. System.out.println(s1 == s2);
  12. System.out.println(s1);
  13. System.out.println(s2);
  14. es.shutdown();
  15. }
  16. }

image.png
静态内部类形式:在内部类被加载和初始化时,才创建 INSTANCE 实例对象
静态内部类不会自动随着外部类的加载和初始化而初始化,它是要单独去加载和初始化的
因为是在内部类加载和初始化时创建的,因此是线程安全的

  1. package s01.e02;
  2. public class Singleton6 {
  3. private Singleton6() {
  4. }
  5. private static class Inner {
  6. private static final Singleton6 INSTANCE = new Singleton6();
  7. }
  8. public static Singleton6 getInstance() {
  9. return Inner.INSTANCE;
  10. }
  11. }

2.6 小结

  • 如果是饿汉式,枚举形式最简单
  • 如果是懒汉式,静态内部类形式最简单