单列设计模式

编程题:写一个Singleton示例

什么是Singleton?

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

要点

  1. 某个类只能有一个实例;
    1. 构造器私有化
  2. 它必须自行创建这个实例;
    1. 含有一个该类的静态变量来保存这个唯一的实例
  3. 它必须自行向整个系统提供这个实例;
    1. 对外提供获取该实例对象的方式:

(1)直接暴露(2)用静态变量的get方法获取

几种常见形式

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

  1. 直接实例化饿汉式(简洁直观)
package com.www.interview.one.singletoncode;

/**
 * 饿汉式
 * <p>
 * 在类初始化时 直接创建实例对象, 不管需不需要都会创建 (简洁直观)
 * <p>
 * 1、私有化构造方法
 * <p>
 * 2、自行创建,并使用静态变量保存
 * <p>
 * 3、向外提供这个实列
 * <p>
 * 4、强调这是一个单例, 用 final 修饰
 * <p>
 *
 * @author Www
 * <p>
 * 邮箱: 483223455@qq.com
 * <p>
 * 创建时间: 2022/8/20  14:49  星期六
 * <p>
 */
public class SingletonDemo {
    public static final SingletonDemo INSTANCE = new SingletonDemo();
    
    /**
     * 私有化构造方法
     */
    private SingletonDemo() {
    
    }
    
}

  1. 枚举式(最简洁)
package com.www.interview.one.singletoncode;

/**
 * 枚举类型:表示该类型的对象是有限的几个 (最简洁)
 * <p>
 * 限定为 一个就成为了单例
 * <p>
 *
 * @author Www
 * <p>
 * 邮箱: 483223455@qq.com
 * <p>
 * 创建时间: 2022/8/20  14:56  星期六
 * <p>
 */
public enum SingletonDemo1 {
    /**
     * INSTANCE
     */
    INSTANCE;
}

  1. 静态代码块饿汉式(适合复杂实例化)
package com.www.interview.one.singletoncode;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 静态代码块饿汉式 (适合复杂实例化)
 * <p>
 *
 * @author Www
 * <p>
 * 邮箱: 483223455@qq.com
 * <p>
 * 创建时间: 2022/8/20  15:05  星期六
 * <p>
 */
public class SingletonDemo2 {
    public static final SingletonDemo2 INSTANCE;
    
    static {
        // 复杂的实例化
        try {
            // 读取配置文件
            Properties properties = new Properties();
            InputStream singleton = ClassLoader.getSystemResourceAsStream("singleton.properties");
            properties.load(singleton);
            String name = properties.getProperty("name");
            INSTANCE = new SingletonDemo2(name);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        
    }
    
    private String name;
    
    private SingletonDemo2(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "SingletonDemo2{" + "name='" + name + '\'' + '}';
    }
}

singleton.properties

name=www

测试


    /**
     * SingletonDemo
     */
    @Test
    public void singletonDemoTest() {
        SingletonDemo instance = SingletonDemo.INSTANCE;
        SingletonDemo instance1 = SingletonDemo.INSTANCE;
        SingletonDemo instance2 = SingletonDemo.INSTANCE;
        // instance = com.www.interview.one.singletoncode.SingletonDemo@7276c8cd
        System.out.println("instance = " + instance);
        // instance1 = com.www.interview.one.singletoncode.SingletonDemo@7276c8cd
        System.out.println("instance1 = " + instance1);
        // instance2 = com.www.interview.one.singletoncode.SingletonDemo@7276c8cd
        System.out.println("instance2 = " + instance2);
    }
    
    /**
     * SingletonDemo1
     */
    @Test
    public void singletonDemo1Test() {
        SingletonDemo1 instance = SingletonDemo1.INSTANCE;
        SingletonDemo1 instance1 = SingletonDemo1.INSTANCE;
        SingletonDemo1 instance2 = SingletonDemo1.INSTANCE;
        // instance = INSTANCE
        System.out.println("instance = " + instance);
        // instance1 = INSTANCE
        System.out.println("instance1 = " + instance1);
        // instance2 = INSTANCE
        System.out.println("instance2 = " + instance2);
    }
    
    /**
     *
     */
    @Test
    public void singletonDemo2Test() {
        SingletonDemo2 instance = SingletonDemo2.INSTANCE;
        // instance = SingletonDemo2{name='www'}
        System.out.println("instance = " + instance);
    }

2. 懒汉式延迟创建对象

  1. 线程不安全(适用于单线程)
package com.www.interview.one.singletoncode;

/**
 * 懒汉式 :
 * <p>
 * 延迟创建这个实例
 * <p>
 * 1、构造器私有化
 * <p>
 * 2、用一个静态变量保存这个唯一的实例
 * <p>
 * 3、提供一个静态方法,获取这个实例对象
 * <p>
 *
 * @author Www
 * <p>
 * 邮箱: 483223455@qq.com
 * <p>
 * 创建时间: 2022/8/20  15:46  星期六
 * <p>
 */
public class SingletonDemo3 {
    private static SingletonDemo3 instance;
    
    /**
     * 私有化构造方法
     */
    private SingletonDemo3() {
        
    }
    
    /**
     * 对外提供一个静态的方法获取实例对象
     * <p>
     * 【 存在线程安全问题】
     *
     * @return SingletonDemo3
     */
    public static SingletonDemo3 getInstance() {
        if (instance == null) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            instance = new SingletonDemo3();
        }
        return instance;
    }
}

  1. 线程安全(适用于多线程)
package com.www.interview.one.singletoncode;

/**
 * 懒汉式 : 【加锁 :解决线程安全问题】
 * <p>
 * 延迟创建这个实例
 * <p>
 * 1、构造器私有化
 * <p>
 * 2、用一个静态变量保存这个唯一的实例
 * <p>
 * 3、提供一个静态方法,获取这个实例对象
 * <p>
 *
 * @author Www
 * <p>
 * 邮箱: 483223455@qq.com
 * <p>
 * 创建时间: 2022/8/20  15:46  星期六
 * <p>
 */
public class SingletonDemo4 {
    private static volatile SingletonDemo4 instance;
    
    /**
     * 私有化构造方法
     */
    private SingletonDemo4() {
        
    }
    
    /**
     * 对外提供一个静态的方法获取实例对象
     * <p>
     * 【 存在线程安全问题】
     *
     * @return SingletonDemo3
     */
    public static SingletonDemo4 getInstance() {
        // 提高性能
        if (instance == null) {
            synchronized (SingletonDemo4.class) {
                if (instance == null) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    instance = new SingletonDemo4();
                }
            }
        }
        return instance;
    }
    /*public static synchronized SingletonDemo4 getInstance() {
        // 提高性能
        if (instance == null) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            instance = new SingletonDemo4();
        }
        return instance;
    }*/
    
}

  1. 静态内部类形式(适用于多线程)
package com.www.interview.one.singletoncode;

/**
 * 在内部类被加载 和 初始化时,才创建 INSTANCE
 * <p>
 * 静态内部类不会自动随着外部类加载和初始化而初始化,他是要单独去加载和初始化的
 * <p>
 * 因为实在内部类加载和初始化时创建的, 所以是线程安全的
 *
 * <p>
 *
 * @author Www
 * <p>
 * 邮箱: 483223455@qq.com
 * <p>
 * 创建时间: 2022/8/20  16:10  星期六
 * <p>
 */
public class SingletonDemo5 {
    private SingletonDemo5() {
    
    }
    
    /**
     * 提供 公共静态方法,获取实例化
     *
     * @return SingletonDemo5
     */
    public static SingletonDemo5 getInstance() {
        return Inner.INSTANCE;
    }
    
    /**
     * 静态内部类
     */
    private static class Inner {
        private static final SingletonDemo5 INSTANCE = new SingletonDemo5();
    }
}

测试


    /**
     * 存在线程安全问题
     */
    @Test
    public void singletonDemo3Test() throws ExecutionException, InterruptedException {
        /*SingletonDemo3 instance = SingletonDemo3.getInstance();
        SingletonDemo3 instance1 = SingletonDemo3.getInstance();*/
        Callable<SingletonDemo3> callable = SingletonDemo3::getInstance;
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Future<SingletonDemo3> f1 = executorService.submit(callable);
        Future<SingletonDemo3> f2 = executorService.submit(callable);
        SingletonDemo3 instance = f1.get();
        SingletonDemo3 instance1 = f2.get();
        // true
        System.out.println(instance == instance1);
        // instance1 = com.www.interview.one.singletoncode.SingletonDemo3@7276c8cd
        System.out.println("instance1 = " + instance1);
        // instance = com.www.interview.one.singletoncode.SingletonDemo3@7276c8cd
        System.out.println("instance = " + instance);
        executorService.shutdown();
        
    }
    
    @Test
    public void singletonDemo4Test() throws ExecutionException, InterruptedException {
        Callable<SingletonDemo4> callable = SingletonDemo4::getInstance;
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Future<SingletonDemo4> f1 = executorService.submit(callable);
        Future<SingletonDemo4> f2 = executorService.submit(callable);
        SingletonDemo4 instance = f1.get();
        SingletonDemo4 instance1 = f2.get();
        // 概率性的出现线程问题
        System.out.println(instance == instance1);
        System.out.println("instance1 = " + instance1);
        System.out.println("instance = " + instance);
        executorService.shutdown();
        
    }
    
    /**
     *
     */
    @Test
    public void singletonDemo5Test() throws ExecutionException, InterruptedException {
        Callable<SingletonDemo5> callable = SingletonDemo5::getInstance;
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Future<SingletonDemo5> f1 = executorService.submit(callable);
        Future<SingletonDemo5> f2 = executorService.submit(callable);
        SingletonDemo5 instance = f1.get();
        SingletonDemo5 instance1 = f2.get();
        // true
        System.out.println(instance == instance1);
        // instance1 = com.www.interview.one.singletoncode.SingletonDemo5@401e7803
        System.out.println("instance1 = " + instance1);
        // instance = com.www.interview.one.singletoncode.SingletonDemo5@401e7803
        System.out.println("instance = " + instance);
        executorService.shutdown();
        
    }

小结

  1. 如果是饿汉式,枚举形式最简单
  2. 如果是懒汉式,静态内部类形式最简单
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一、学习目的 单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保...
    颵麏阅读 2,795评论 0 2
  • 单例模式 定义:是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。 饿汉式单例模式 类加载的时候...
    竹blue阅读 2,372评论 0 1
  • 文章首发:设计模式之单列模式[https://www.maishuren.top/archives/%E8%AE%...
    HeyChee阅读 949评论 0 0
  • 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 单例模式的使用很广泛,比如:线程池(threa...
    richy_阅读 2,454评论 0 0
  • 单例模式(SingletonPattern)一般被认为是最简单、最易理解的设计模式,也因为它的简洁易懂,是项目中最...
    成热了阅读 9,725评论 4 34