单态模式可能是23种创建模式种最简单和容易理解的,创建型模式是主要是为了解决对象的创建的方式,单态则是为了保证创建的对象的唯一性。
模式场景与说明
有早期编码经验的开发者一般在N层应用开发中的业务逻辑层和持久层之间会有数据库连接的获取,不管是从缓冲池中获取还是直接连接JDBC,为了避免创建多个示例产生资源的浪费,一般会使用单态模式对创建对象进行控制。实际上除此之外,在类似的池化控制中都可以类似的引入,比如线程池等。此类对象创建一般存在:重(类似数据库连接的获取)、频(使用程度较为频繁)的特点。
实现方式1
单态模式实现非常简单,一般只要把握两个要点,就能快速创建一个单态模式的类:
- 控制构造函数:私有化构造函数保证无法随意创建对象
- 统一创建对象:在类中提供对象的构建方法并提供公有方法返回创建的实例(一般为静态函数)
实现示例
最简单的示例代码如下所示
public class Singleton {
// use private key word to avoid being used directly
private Singleton() {}
private static Singleton instance = new Singleton();
// use public method to get singleton instance
public static Singleton getInstance(){
return instance;
}
}
调用方式:Singleton.getInstance();
这里稍微修改一下,显示一下输出内容,特意在构建函数里添加sleep,可以在多个终端同时启动,可以验证并行时仍然使用同一个实例(hashcode相同)。
package com.liumiao;
import java.time.LocalDateTime;
public class Singleton {
// use private key word to avoid being used directly
private Singleton() {
System.out.printf("%s: Single instance creating begins... \n" ,LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s: Single instance creating ends... \n" ,LocalDateTime.now());
}
private static Singleton instance = new Singleton();
// use public method to get singleton instance
public static Singleton getInstance(){
System.out.println("get Singleton instance: " + LocalDateTime.now());
System.out.println(instance.hashCode());
return instance;
}
}
测试代码如下
package com.liumiao;
public class TestSingleton extends Thread {
public void run() {
Singleton.getInstance();
}
public static void main( String[] args )
{
TestSingleton thread1 = new TestSingleton ();
TestSingleton thread2 = new TestSingleton ();
thread1.start();
thread2.start();
}
}
执行结果如下
2020-06-26T05:37:58.555119: Single instance creating begins...
2020-06-26T05:37:59.583467: Single instance creating ends...
get Singleton instance: 2020-06-26T05:37:59.583739
140350290
get Singleton instance: 2020-06-26T05:37:59.583756
140350290
实现方式2
由于实现方式1中对于实例的构建在类装载的时候就已经完成,虽然解决了线程的同步,但是即使没有使用到这个实例也会加载,这里修改一下将new的动作移到getInstance中,通过是否为空来判断一下是否需要new进行构建对象,示例代码如下所示:
package com.liumiao;
import java.time.LocalDateTime;
public class Singleton {
// use private key word to avoid being used directly
private Singleton() {
System.out.printf("%s: Single instance creating begins... \n" ,LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s: Single instance creating ends... \n" ,LocalDateTime.now());
}
private static Singleton instance = null;
// use public method to get singleton instance
public static Singleton getInstance(){
System.out.println("get Singleton instance: " + LocalDateTime.now());
if (null == instance) {
instance = new Singleton();
}
System.out.println(instance.hashCode());
return instance;
}
}
使用同样的测试代码,执行结果如下,所示
get Singleton instance: 2020-06-26T05:39:00.137037
get Singleton instance: 2020-06-26T05:39:00.137051
2020-06-26T05:39:00.137567: Single instance creating begins...
2020-06-26T05:39:00.137614: Single instance creating begins...
2020-06-26T05:39:01.162630: Single instance creating ends...
140350290
2020-06-26T05:39:01.162630: Single instance creating ends...
1253458049
可以看到,单态出现了问题,产生了不同的实例(hashcode不同),因为没有考虑到线程安全的问题
实现方式3
这里简单地使用synchronized来稍作修改, 代码如下
package com.liumiao;
import java.time.LocalDateTime;
public class Singleton {
// use private key word to avoid being used directly
private Singleton() {
System.out.printf("%s: Single instance creating begins... \n" ,LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s: Single instance creating ends... \n" ,LocalDateTime.now());
}
private static Singleton instance = null;
// use public method to get singleton instance
public static synchronized Singleton getInstance(){
System.out.println("get Singleton instance: " + LocalDateTime.now());
if (null == instance) {
instance = new Singleton();
}
System.out.println(instance.hashCode());
return instance;
}
}
使用同样的测试代码执行结果如下
get Singleton instance: 2020-06-26T05:42:13.732276
2020-06-26T05:42:13.732766: Single instance creating begins...
2020-06-26T05:42:14.758235: Single instance creating ends...
346203776
get Singleton instance: 2020-06-26T05:42:14.758679
346203776
所以可以看到已经返回了相同的实例了