文档章节

设计模式:单例模式

人觉非常君
 人觉非常君
发布于 07/17 23:36
字数 920
阅读 5
收藏 0

单例模式的定义是确保某个类在任何情况下都只有一个实例,并且需要提供一个全局的访问点供调用者访问该实例的一种模式。

实现以上模式基于以下必须遵守的两点:

1.构造方法私有化

2.提供一个public static修饰的方法,来返回实例

单例模式进化史:

起初是饥汉模式

package com.demo;

/**
 * 饥汉模式
 * 缺点:无法延迟加载
 * @author huang
 *
 */
public class Demo01 {
	
	private Demo01() {}

	private static Demo01 test01 = new Demo01();
	
	public static Demo01 getInstance() {
		return test01;
	}
	
}

为了提高效率,需要延迟加载,就出现了懒汉模式

package com.demo;

/**
 * 懒汉模式
 * 缺点:线程不安全
 * @author huang
 *
 */
public class Demo02 {
	
	private Demo02() {}

	private static Demo02 test01 = null;
	
	public static Demo02 getInstance() {
		return (null == test01) ? new Demo02() : test01;
	}
	
}

效率可以了,线程又不安全了,继续改

package com.demo;

/**
 * 缺点:高并发情况下性能差
 * @author huang
 *
 */
public class Demo03 {
	
	private Demo03() {}

	private static Demo03 test01 = null;
	
	public synchronized static Demo03 getInstance() {
		return (null == test01) ? new Demo03() : test01;
	}
	
}

synchronized关键字修饰的是方法,效率又下去了

package com.demo;

/**
 * 饥汉模式
 * 缺点:JVM重排序的机制,可能会返回没有初始化的对象
 * @author huang
 *
 */
public class Demo04 {
	
	private Demo04() {}

	private static Demo04 test01 = null;
	
	public static Demo04 getInstance() {
		if(null == test01) {
			synchronized (Demo04.class) {
				if(null == test01) {
					test01 = new Demo04();
				}
			}
		}
		return test01;
	}
}

改用synchronized关键字修饰代码块,而且还进行了双次校验,可惜呀,有可能发生重排序,导致返回的是一个没有初始化的实例对象

继续改进:使用volatile关键字可以禁止重排序

package com.demo;

/**
 * 饥汉模式
 * 优点:线程安全
 * 缺点:通过反射获取的对象与调用getInstance()返回的对象不是同一个
 * @author huang
 *
 */
public class Demo05 {
	
	private Demo05() {}

	private static volatile Demo05 test01 = null;
	
	public static Demo05 getInstance() {
		if(null == test01) {
			synchronized (Demo05.class) {
				if(null == test01) {
					test01 = new Demo05();
				}
			}
		}
		return test01;
	}
}

另外:Java虚拟机在进行类的加载过程中,会执行类的初始化。在执行初始化期间,Java虚拟机可以同步多个线程对一个类的初始化,保证类的初始化的线程安全性。所以我们也可以这么玩

package com.demo;

/**
 * 饥汉模式
 * 优点:线程安全
 * 缺点:通过反射获取的对象与调用getInstance()返回的对象不是同一个
 * @author huang
 *
 */
public class Demo06 {
	
	private Demo06() {}

	
	public synchronized static Demo06 getInstance() {
		return InnerClass06.test01;
	}
	
	private static class InnerClass06 {
		private static Demo06 test01 = new Demo06();
	}
	
}

然后,还是还是有漏洞
 

package com.test;

import java.lang.reflect.Constructor;

import com.demo.Demo06;

public class Ctest06 {

	public static void main(String[] args) throws Exception {
		Demo06 o1 = Demo06.getInstance();
		Demo06 o2 = Demo06.getInstance();
		System.out.println("o1==o2:"+(o1 == o2));// true
		// 没抵挡住反射的诱惑啊
		Constructor<Demo06> declaredConstructors = Demo06.class.getDeclaredConstructor();
		declaredConstructors.setAccessible(true);
		Demo06 o3 = declaredConstructors.newInstance();
		System.out.println("o1==o3:"+(o1 == o3));// false
	}
	
}

执行以上代码,我们通过反射可以得到不同的实例,这就违背了我们单例模式的初衷。

最后,我们只能利用枚举了,枚举类无法被反射

package com.bean;

public class User {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
package com.demo;

import com.bean.User;

/**
 * 饥汉模式
 * 优点:线程安全 Enum无法通过反射来获取实例
 * @author huang
 *
 */
public enum Demo07 {
	
	SINGLETON;
	
	private User user;

	private Demo07() {
		user = new User();
	}
	
	public User getInstance() {
		return user;
	}
	
}
package com.test;

import com.bean.User;
import com.demo.Demo07;

public class Ctest07 {

	public static void main(String[] args) {
		User demo1 = Demo07.SINGLETON.getInstance();
		User demo2 = Demo07.SINGLETON.getInstance();
		System.out.println("demo1==demo2:"+(demo1 == demo2));// true
	}

}

推荐使用枚举来实现单例模式

© 著作权归作者所有

共有 人打赏支持
人觉非常君
粉丝 6
博文 43
码字总数 29951
作品 0
浦东
私信 提问
【设计模式笔记】(十六)- 代理模式

一、简述 代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。 其实代理模式无论是在日常开发还是设计模式中,基本随处可见,中介者模式中...

MrTrying
06/24
0
0
设计模式已经陨落了?

你现在是坐在一个程序员旁边吗?如果是的话,那么在你读下面的段落之前,有一个简单的实验。让他们到一边去,问问他们两个问题并记录下答案。首先问他们“什么是设计模式?”然后再问“说出你...

oschina
2014/03/11
9.1K
69
《PHP设计模式大全》系列分享专栏

《PHP设计模式大全》已整理成PDF文档,点击可直接下载至本地查阅 https://www.webfalse.com/read/201739.html 文章 php设计模式介绍之编程惯用法第1/3页 php设计模式介绍之值对象模式第1/5页...

kaixin_code
11/06
0
0
设计模式梳理(一)

设计模式梳理(一) 总体来说设计模式分为三大类: @案例源码地址:https://gitlab.com/lxqxsyu/DisgnPattern 创建型模式 简单工厂模式 工厂类是整个模式的关键。它包含必要的判断逻辑,能够...

lxq_xsyu
2017/11/02
0
0
Ubuntu中vi卸载与安装/使用模式

Ubuntu中安装的vi是vim-common版本,与centos系统中vi使用方式不同,编辑使用不惯, 遂卸载重装,卸载命令:sudo apt-get remove vim-common 卸载完毕后重新安装;输入命令:sudo apt-get in...

唐十三郎
11/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

PHP生成CSV之内部换行

当我们使用PHP将采集到的文件内容保存到csv文件时,往往需要将采集内容进行二次过滤处理才能得到需要的内容。比如网页中的换行符,空格符等等。 对于空格等处理起来都比较简单,这里我们单独...

豆花饭烧土豆
今天
2
0
使用 mjml 生成 thymeleaf 邮件框架模板

发邮件算是系统开发的一个基本需求了,不过搞邮件模板实在是件恶心事,估计搞过的同仁都有体会。 得支持多种客户端 支持响应式 疼彻心扉的 outlook 多数客户端只支持 inline 形式的 css 布局...

郁也风
今天
8
0
让哲学照亮我们的人生——读《医务工作者需要学点哲学》有感2600字

让哲学照亮我们的人生——读《医务工作者需要学点哲学》有感2600字: 作者:孙冬梅;以前读韩国前总统朴槿惠的著作《绝望锻炼了我》时,里面有一句话令我印象深刻,她说“在我最困难的时期,...

原创小博客
今天
4
0
JAVA-四元数类

public class Quaternion { private final double x0, x1, x2, x3; // 四元数构造函数 public Quaternion(double x0, double x1, double x2, double x3) { this.x0 = ......

Pulsar-V
今天
18
0
Xshell利用Xftp传输文件,使用pure-ftpd搭建ftp服务

Xftp传输文件 如果已经通过Xshell登录到服务器,此时可以使用快捷键ctrl+alt+f 打开Xftp并展示Xshell当前的目录,之后直接拖拽传输文件即可。 pure-ftpd搭建ftp服务 pure-ftpd要比vsftp简单,...

野雪球
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部