JAVA设计模式之单例模式

一.文章推荐

文章推荐

二.创建型模式

创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。

创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。

2.1 单例模式

2.1.1单例模式定义

保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

2.1.2 为什么使用单例模式

  1. 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决.
  2. 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理.

2.1.3 为什么不使用全局变量保证一个类只有一个实例

我们知道全局变量分为静态变量和实例变量,静态变量也可以保证该类的实例只存在一个。
只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。

但是,如果说这个对象非常消耗资源,而且程序某次的执行中一直没用,这样就造成了资源的浪费。利用单例模式的话,我们就可以实现在需要使用时才创建对象,这样就避免了不必要的资源浪费。 不仅仅是因为这个原因,在程序中我们要尽量避免全局变量的使用,大量使用全局变量给程序的调试、维护等带来困难。

三.单例模式的实现

通常单例模式在Java语言中,有两种构建方式:

**饿汉方式:**指全局的单例实例在类装载时构建
**懒汉方式:**指全局的单例实例在第一次被使用时构建。
不管是那种创建方式,它们通常都存在下面几点相似处:

  1. 单例类必须要有一个 private 访问级别的构造函数,只有这样,才能确保单例不会在系统中的其他代码内被实例化;
  2. instance 成员变量和 uniqueInstance 方法必须是 static 的。

3.1 饿汉式实现(单例对象立即加载)

public class SingletonDemo02 {
	private static /*final*/ SingletonDemo02 s = new SingletonDemo02();
	
	private SingletonDemo02(){} //私有化构造器
	
	public static /*synchronized*/ SingletonDemo02 getInstance(){
		return s;
	}
}

饿汉式单例模式代码中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省synchronized关键字。

问题:如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费!

3.2 懒汉式实现(单例对象延迟加载)

public class SingletonDemo01 {
	private static SingletonDemo01 s;
	
	private SingletonDemo01(){} //私有化构造器
	
	public static synchronized SingletonDemo01 getInstance(){
		if(s==null){
		s = new SingletonDemo01();
		}
	return s;
	}
}

要点:lazy load! 延迟加载, 懒加载! 真正用的时候才加载!

问题:资源利用率高了。但是,每次调用getInstance()方法都要同步,并发效率较低。

3.3 懒汉式(双重检查加锁版本)

利用双重检查加锁(double-checked locking),首先检查是否实例已经创建,如果尚未创建,“才”进行同步。这样以来,只有一次同步,这正是我们想要的效果。

public class Singleton {

//volatile保证,当uniqueInstance变量被初始化成Singleton实例时,多个线程可以正确处理uniqueInstance变量
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getInstance() {
   //检查实例,如果不存在,就进入同步代码块
    if (uniqueInstance == null) {
        //只有第一次才彻底执行这里的代码
        synchronized(Singleton.class) {
           //进入同步代码块后,再检查一次,如果仍是null,才创建实例
            if (uniqueInstance == null) {
                uniqueInstance = new Singleton();
            }
        }
    }
    return uniqueInstance;
}

}

3.4 静态内部类实现方式(也是一种懒加载方式)

public class SingletonDemo04 {

	private static class SingletonClassInstance {
	
	private static final SingletonDemo04 instance = new SingletonDemo04();
}

	public static SingletonDemo04 getInstance() {
		return SingletonClassInstance.instance;
	}
	
	private SingletonDemo04() {
	}
}

要点

  1. 外部类没有static属性,则不会像饿汉式那样立即加载对象。
  2. 只有真正调用getInstance(),才会加载静态内部类。加载类时是线程 安全的。 instance是static final
    类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性. – 兼备了并发高效调用和延迟加载的优势!

3.5 使用枚举实现单例模式

	public enum SingletonDemo05 {
	
		/**
		* 定义一个枚举的元素,它就代表了Singleton的一个实例。
		*/
		INSTANCE;
		
		/**
		* 单例可以有自己的操作
		*/
		public void singletonOperation(){
				//功能处理
		}
}

使用方法:

public static void main(String[] args) {

	SingletonDemo05 sd = SingletonDemo05.INSTANCE;
	SingletonDemo05 sd2 = SingletonDemo05.INSTANCE;
	System.out.println(sd==sd2);
}
  1. 优点: 实现简单:枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
  2. 缺点:无延迟加载

四.总结

主要
• 饿汉式(线程安全,调用效率高。 但是,不能延时加载。)
• 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)

其他
• 静态内部类式(线程安全,调用效率高。 但是,可以延时加载)
• 枚举单例(线程安全,调用效率高,不能延时加载)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值