设计模式之单例模式
2015-11-30 18:30
232 查看
一.概念
通俗的的讲,单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
二.实现
打个比方:一个国家只允许有一个皇帝,对应我们的代码就是一个类只能产生一个对象。
怎么实现呢,对象都是通过new 关键字产生的(当然还有别的方法,例如对象复制,反射等),这可怎么控制啊?方法当然有啦,大家可别忘了还有个构造函数,使用new关键字的时候,都会调用构造函数,如果我们把构造函数设置为private私有访问权限的话,不就可以禁止外部创建对象了吗?
代码如下:
public class Emperor { //皇帝类
private static Emperor emperor; //初始化一个皇帝
private Emperor(){
//看到没,这个构造方法是private ,目的就是不希望产生第二个皇帝
}
public static Emperor getInstance(){
if(emperor==null){
emperor = new Emperor
}
return emperor;
}
//皇帝发话了
public static void say(){
System.out.println("我就是皇帝小明....");
}
}
在这里,我们通过定义一个私有访问权限的构造函数,避免被其他类new出来一个对象,而Emperor自己则可以new一个对象出来,其他类对该类的访问都可以通过getInstance获得同一个对象。
大家来猜猜一下运行结果是什么?
public class Client {
public static void main(String[] args) {
for(int day=0;day<3;day++){
Emperor emperor=Emperor.getInstance();
emperor.say();
}
}
}
运行结果如下所示。
我就是皇帝小明....
我就是皇帝小明....
我就是皇帝小明....
好了这就是单例模式,就是这么简单!
三.扩展
单例模式在高并发的情况下,在内存中很可能出现多个“皇帝”实例,破坏了我们最初的预期。为什么会出现这种情况呢?
如一个线程A执行 到singleton = new Singleton(),但还没有获得对象(对象初始化是需要时间的),第二个线程 B也在执行,执行到(singleton == null)判断,那么线程B获得判断条件也是为真,于是继续 运行下去,线程A获得了一个对象,线程B也获得了一个对象,在内存中就出现两个对象!
如何解决这种问题呢?方法很多,这里我们给出3种方法:
方法1. 建议使用这一种
public class Emperor {
private static final Emperor emperor = new Emperor ();
//限制产生多个对象
private Emperor (){
}
//通过该方法获得实例对象
public static Emperor getInstance(){
return emperor ;
}
//类中其他方法,尽量是static
public static void doSomething(){}
}
方法2:
public class Emperor {
private Emperor emperor ;
//限制产生多个对象
private Emperor (){
}
//通过该方法获得实例对象
public static Emperor getInstance(){
if (emperor == null) {
synchronized (Emperor .class) {
if (emperor == null) {
emperor = newEmperor ();
}
}
}
return defaultInstance;
}
}
方法3:
public class Emperor {
private Emperor emperor ;
//限制产生多个对象
private Emperor (){}
//通过该方法获得实例对象
public synchronized static Emperor getInstance(){
if (emperor == null) {
emperor = new Emperor ();
}
return
emperor ;
}
}
四.结尾
单例就是这么简单,谢谢大家指正。
通俗的的讲,单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
二.实现
打个比方:一个国家只允许有一个皇帝,对应我们的代码就是一个类只能产生一个对象。
怎么实现呢,对象都是通过new 关键字产生的(当然还有别的方法,例如对象复制,反射等),这可怎么控制啊?方法当然有啦,大家可别忘了还有个构造函数,使用new关键字的时候,都会调用构造函数,如果我们把构造函数设置为private私有访问权限的话,不就可以禁止外部创建对象了吗?
代码如下:
public class Emperor { //皇帝类
private static Emperor emperor; //初始化一个皇帝
private Emperor(){
//看到没,这个构造方法是private ,目的就是不希望产生第二个皇帝
}
public static Emperor getInstance(){
if(emperor==null){
emperor = new Emperor
}
return emperor;
}
//皇帝发话了
public static void say(){
System.out.println("我就是皇帝小明....");
}
}
在这里,我们通过定义一个私有访问权限的构造函数,避免被其他类new出来一个对象,而Emperor自己则可以new一个对象出来,其他类对该类的访问都可以通过getInstance获得同一个对象。
大家来猜猜一下运行结果是什么?
public class Client {
public static void main(String[] args) {
for(int day=0;day<3;day++){
Emperor emperor=Emperor.getInstance();
emperor.say();
}
}
}
运行结果如下所示。
我就是皇帝小明....
我就是皇帝小明....
我就是皇帝小明....
好了这就是单例模式,就是这么简单!
三.扩展
单例模式在高并发的情况下,在内存中很可能出现多个“皇帝”实例,破坏了我们最初的预期。为什么会出现这种情况呢?
如一个线程A执行 到singleton = new Singleton(),但还没有获得对象(对象初始化是需要时间的),第二个线程 B也在执行,执行到(singleton == null)判断,那么线程B获得判断条件也是为真,于是继续 运行下去,线程A获得了一个对象,线程B也获得了一个对象,在内存中就出现两个对象!
如何解决这种问题呢?方法很多,这里我们给出3种方法:
方法1. 建议使用这一种
public class Emperor {
private static final Emperor emperor = new Emperor ();
//限制产生多个对象
private Emperor (){
}
//通过该方法获得实例对象
public static Emperor getInstance(){
return emperor ;
}
//类中其他方法,尽量是static
public static void doSomething(){}
}
方法2:
public class Emperor {
private Emperor emperor ;
//限制产生多个对象
private Emperor (){
}
//通过该方法获得实例对象
public static Emperor getInstance(){
if (emperor == null) {
synchronized (Emperor .class) {
if (emperor == null) {
emperor = newEmperor ();
}
}
}
return defaultInstance;
}
}
方法3:
public class Emperor {
private Emperor emperor ;
//限制产生多个对象
private Emperor (){}
//通过该方法获得实例对象
public synchronized static Emperor getInstance(){
if (emperor == null) {
emperor = new Emperor ();
}
return
emperor ;
}
}
四.结尾
单例就是这么简单,谢谢大家指正。
相关文章推荐
- JSP 最佳实践: 用 jsp:include 控制动态内容
- 百度地图Android SDK报错:Error inflating class com.baidu.mapapi.map.MapView
- Redhat7 配置软RAID(mdadm)
- php js特殊字符处理
- Unreal3 window下内存管理实现详解
- 基于统计的分词技术
- ios应用:release与debug编译方式的区别
- 2015第49周一
- spark使用总结
- spring启用线程空指针异常
- Java进阶05 多线程
- 密码输入页面的实现-模仿支付宝
- 【LeetCode】300 Longest Increasing Subsequence
- JSP 最佳实践: 使用JSTL来更新JSP页面
- 浅析 Linux 初始化 init 系统,第 1 部分: sysvinit
- 【Objective_C学习笔记】Block的使用
- C语言中strdup函数使用方法
- Spark编程进阶
- 别再迷信 zepto 了
- 镜头光晕是如何形成的?