被说了很多遍的设计模式---单例模式

[把你的理性思维慢慢变成条件反射]

本文,我们讲介绍单例模式,文章主题结构与上文一致。惯例,先来看看我们示例工程的环境:

操作系统:win7 x64

其他软件:eclipse mars,jdk8

-------------------------------------------------------------------------------------------------------------------------------------

经典问题:

日志Logger对象。

思路分析:

要点一:在任意时刻,系统中只能有数量小于等于一个的该对象。

要点二:无论是自己创建,还是被其他对象创建或引用,均不能破坏唯一性。

【由于单例模式是较为常用的一种设计模式。所以我们调整一下顺序,先说明概念,然后在展示示例代码】

模式总结:

单例模式结构图:


单例模式:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

组成部分:

       Singleton(单例类):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。

模板代码:

懒汉式单例:

特点:

  1. 延迟加载,只有到真正使用是才生成一个单例对象供调用对象使用。并且,以后不会再生成新的该对象。
  2. 拥有一个private修饰的静态变量
  3. 拥有一个private修饰的构造方法,方式被外部的对象调用。
  4. 拥有一个public修饰的静态工厂方法,返回一个具体的实例。

package com.csdn.ingo.gof_Singleton;

public class Singleton {
	private static Singleton instance;
	
	private Singleton(){}
	
	public static Singleton getInstance(){
		if(instance==null){
			instance = new Singleton();
		}
		return instance;
	}
}
package com.csdn.ingo.gof_Singleton;

public class WIndow {
	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		
		if(s1==s2){
			System.out.println("s1==s2");
		}
	}
}
       Singleton通过将构造方法限定为private避免了本类在外部进行实例化操作,在同一个虚拟机的范围内,Singleton的唯一示例只能通过getInstance()方法进行获取。(事实上,通过Java的反射机制,能够访问private类型的方法,此处对此问题暂不做讨论)

懒汉式单例的问题:

       当发生多线程同时使用该对象时,会发生线程不安全的问题。很有可能生成多个对象。

解法:

       增加线程锁,双重锁检查,静态内部类(推荐)等方法。

①增加同步锁:

public static synchronized Singleton getInstance() {  
         if (single == null) {    
             single = new Singleton();  
         }    
        return single;  
} 
②双重锁检查:

public static Singleton getInstance() {  
        if (singleton == null) {    
            synchronized (Singleton.class) {    
               if (singleton == null) {    
                  singleton = new Singleton();   
               }    
            }    
        }    
        return singleton;   
 }  
③静态内部类:(内部类在调用时才进行加载)
public class Singleton {    
    private static class LazyHolder {    
       private static final Singleton INSTANCE = new Singleton();    
    }    
    private Singleton (){}    
    public static final Singleton getInstance() {    
       return LazyHolder.INSTANCE;    
    }    
} 

饿汉式单例:

特点:

  1. 在类进行加载时就创建了一个单例对象,并且之后一直使用该对象。
  2. 拥有一个private final static 修饰的,并且自身带初始化的变量。

package com.csdn.ingo.gof_Singleton.two;

public class Singleton {
	
	private Singleton() {
	}

	private final static Singleton instance = new Singleton();

	public static Singleton getInstance() {
		return instance;
	}
}

以上两种单例模式区别:

线程安全性:

       饿汉式的单例模式:一定是线程安全的。懒汉式的单例模式:其自身是非线程安全的,但是能够通过其他①②③三种方式实现安全性。

性能:

       饿汉式的单例模式:

             在启动实例化过程时,就会创建一个单例对象。因此,在时间上而言,实例化启动的时间较长,使用时的调用时间相对较短。另外,该单例对              象会一直存在于内存当中。

      懒汉式的单例模式:

            在启动实例化过程中,没有创建一个单例对象,而是在使用时才创建一个单例对象。因此,在时间上而言,实例化启动的时间较短,使用时的             调用时间相对教程。另外,在实例化之后,该单例对象也会一直存在于内存当中。

除此之外:

上文第①种方式:每次都需要使用同步锁,对性能会产生不利影响。

上文第②种方式:在第一次调用时,使用同步锁。后续不再使用同步锁。对性能影响较为有限。

上文第③种方式:内部类在初始化时未生成一个单例对象,而是等到第一次调用时才生成。在调用时,由于Classloader的特殊性,保证实例化的对象有且只有一个单例对象。因此,其表现效果类似于②。

枚举式单例:

特点:

  1. 通过枚举定义的方式声明与使用单例对象
  2. 线程安全:枚举的创建过程本身是线程安全的
  3. 不会因为序列化而产生新实例:在传统的单例模式中,一旦实现了Serializable接口,其就不再是单例的对象了,因为readObject()方法内次总是返回一个新的示例对象,此过程类似于Java中的构造器。而对于枚举实现单例,其实从JVM中防止了此问题。

package com.csdn.ingo.gof_Singleton.thire;

public enum Singleton {
	Instance; // 定义一个枚举的元素,就代表Singleton的一个实例

	private Singleton() {
		
	}
}

双重锁Volatile检查的单例模式:

特点:

  1. volatile关键字:保证该对象始终是最新的值(在Java1.5版本之后上文的双重锁存在漏洞,具体原因,请各位看官查看volatile解释即可)
  2. 其他类似于上文双重锁检查。

package com.csdn.ingo.gof_Singleton.five;

public class Singleton {
	
	private volatile static Singleton singleton;

	private Singleton() {
	}

	public static Singleton getInstance() {
		if (singleton == null) {
			synchronized (Singleton.class) {
				if (singleton == null) {
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}
}

反思:

应用场景:

  1. 在系统内只需要有且只有一个该对象时,如日志对象,ID生成器对象,等等。
  2. 客户端对单例对象只允许使用一个公共访问点。

优点:

  1. 单例模式提供了对实例对象的唯一入口。严格控制了客户的方式与路径。
  2. 节约系统资源,节约该对象的响应时间。
  3. 单例模式可以扩展为指定数量的对象生成器。其效果类似于连接池的创建过程,取得资源与效率的平衡。

缺点:

  1. 单例模式不符合“开闭原则”。对单例的维护只能修改单例类本身。
  2. 单例模式不符合“单一职责原则”。单例类本身负责创建对象,有包含了对象的其他方法。
  3. Java垃圾回收机制会将长时间未使用的,实例化的共享的单例对象进行回收,导致该对象的状态丢失。

-------------------------------------------------------------------------------------------------------------------------------------

至此,被说了很多遍的设计模式---单例模式 结束


参考资料:

图书:《大话设计模式》

其他博文:

http://blog.csdn.NET/lovelion/article/details/7563445

http://cantellow.iteye.com/blog/838473

https://my.oschina.net/zhoujy/blog/134958

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

http://www.cnblogs.com/zhengbin/p/5654805.html

http://javarevisited.blogspot.jp/2012/07/why-enum-singleton-are-better-in-java.html


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值