编程题:写一个Singleton示例
什么是Singleton?
- Singleton:在Java中即指单例设计模式,它是软件开发中最常用的设计模式之一。
- 单:唯一
- 例:实例
- 单例设计模式,即某个类在整个系统中****只能有一个实例对象可被获取和使用****的代码模式。
- 例如:代表JVM运行环境的Runtime类
要点
- 某个类只能有一个实例;
- 构造器私有化
- 它必须自行创建这个实例;
- 含有一个该类的静态变量来保存这个唯一的实例
- 它必须自行向整个系统提供这个实例;
- 对外提供获取该实例对象的方式:
(1)直接暴露(2)用静态变量的get方法获取
几种常见形式
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() {
}
}
- 枚举式(最简洁)
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;
}
- 静态代码块饿汉式(适合复杂实例化)
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. 懒汉式:延迟创建对象
- 线程不安全(适用于单线程)
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;
}
}
- 线程安全(适用于多线程)
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;
}*/
}
- 静态内部类形式(适用于多线程)
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();
}
小结
- 如果是饿汉式,枚举形式最简单
- 如果是懒汉式,静态内部类形式最简单