版权声明:需要引用、发表的朋友请与本人联系 https://blog.csdn.net/pbrlovejava/article/details/84619518
更多关于设计模式的文章请点击:设计模式之禅(0)-目录页
单件模式是面向对象设计模式中常用的一种设计模式,它主要的作用是使得某个对象在全局程序中只有唯一的一个实例,并且在全局中只有一个创建实例的访问点。这种模式通常采用在线程池、连接池、单一记录器等的使用和实现中。
一、单件模式的实现
单件模式(Singleton Pattern
)在23种设计模式中属于比较简单的那一类,隶属于创建型模式。它的主要思想是在全局中只有一个获得实例的创建点,并且将构造函数私有,不能由外部程序随意地创建它的实例。
- SingletonObject1
/**
* @Description: 单件模式构造
* @CreateDate: Created in 2018/11/29 10:54
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public class SingletonObject1 {
private static SingletonObject1 s;
//构造函数私有,外部类无法直接创建该对象
private SingletonObject1(){
}
//全局唯一的获得实例点
public static SingletonObject1 getInstance(){
//判断对象是否已经被创建,没有被创建则新建对象
if( s == null){
s = new SingletonObject1();
}
return s;
}
}
通过私有化构造器并且提供唯一的实例获得点,就可以实现SingletonObject在全局中只存在唯一的一个实例了。
@Test
public void fun1() {
SingletonObject1 singletonObject1 = SingletonObject1.getInstance();
SingletonObject1 singletonObject2 = SingletonObject1.getInstance();
System.out.println(singletonObject1+"\n"+singletonObject2);
}
二、在并发下单件模式的改进
2.1、使用闭锁测试单件模式的正确性
上面写的单件模式代码似乎已经可以在全局中只产生一个实例了,可是如果在多线程模式下,以上代码还能实现单件吗?我使用了闭锁来测试并发时是否仍然能获得唯一的单件类:【关于闭锁的使用可以阅读我的一篇文章:Java并发编程(9)-使用闭锁测试并发线程安全性】
@Test
public void concurrentTest(){
//开始闭锁
CountDownLatch startLatch = new CountDownLatch(1);
for (int i = 0; i < 5; i++) {
new Thread() {
public void run(){
try {
//线程运行至闭锁处等待
startLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//5个线程并发执行任务
new MyTask().run();
}
}.start();
}
//线程全部集结在闭锁下,开锁
startLatch.countDown();
}
//任务类
public class MyTask implements Runnable{
@Override
public void run() {
//获得单例类
SingletonObject1 instance = SingletonObject1.getInstance();
//打印地址
System.out.println(instance);
}
}
在这个测试中,我只让5个线程去并发地获得单件类实例,可是却出现了问题,单件的实例并不唯一了:
2.2、使用同步锁改进单件模式
要使得单件模式在并发条件下仍然正确,最简单的方式就是使用同步锁来限制同时读取实例的线程数,这个做法很安全,但是会影响速度:
//全局唯一的获得实例点
public static synchronized SingletonObject1 getInstance(){
//判断对象是否已经被创建,没有被创建则新建对象
if( s == null){
s = new SingletonObject1();
}
return s;
}
2.3、使用二重检查加锁来改进单件模式
对于同步锁而言,还可以使用更加轻量的volatile
关键字来实现线程之间对实例状态的检查加锁:
public class SingletonObject {
private volatile static SingletonObject instance;
//构造方法私有化,只能在本类中调用构造方法
private SingletonObject(){
}
/**
*@description 获得全局唯一的实例
*@author arong
*@date 2018/11/27
*@param:
*@return com.iteason.singletonPattern.SingletonObject
*/
public static SingletonObject getInstance(){
//双重检查加锁
if(instance == null){
synchronized (instance){
instance = new SingletonObject();
}
}
return instance;
}
}