单列设计模式

148 阅读5分钟

单列设计模式

编程题:写一个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. 如果是懒汉式,静态内部类形式最简单