设计模式 4 - 单例设计模式及案例分析

目录

 

一、单例设计模式简单介绍

二、单例设计模式的八种方式

1、饿汉式(静态变量)

应用流程

代码实现

优缺点说明

2、饿汉式(静态代码块)

代码演示

优缺点说明

3、懒汉式(线程不安全)

代码演示

优缺点说明

4、懒汉式(线程安全,同步方法)

代码演示

优缺点说明

5、懒汉式(线程安全,同步代码块)

代码演示

说明

6、双重检查

代码演示

优缺点说明

7、静态内部类

代码演示

优缺点说明

8、枚举

代码演示

优缺点说明

三、单例模式在JDK中应用源码分析

java.lang.Runtime就是经典的单例模式(饿汉式)

四、单例模式注意事项和细节说明


一、单例设计模式简单介绍

  • 单例模式指的就是使用一定的方法,保证在一个软件系统中,某一个类的对象实例只能存在一个,并且该类只提供一个获取该类实例的方法。
  • 比如Hibernate中的SessionFactory,它充当了数据存储的代理,并且负责创建session对象,SessionFactory是一个重量级的对象,一般情况下,一个项目就通常只需要一个SessionFactory对象,这可以使用单例模式。

二、单例设计模式的八种方式

  1. 饿汉式(静态变量)
  2. 饿汉式(静态代码块)
  3. 懒汉式(线程不安全)
  4. 懒汉式(线程安全,同步方法)
  5. 懒汉式(线程安全,同步代码块)
  6. 双重检查(volatile+同步代码块+双重if)
  7. 静态内部类
  8. 枚举

1、饿汉式(静态变量)

应用流程

  1. 构造器私有化
  2. 内部创建一个静态变量
  3. 向外暴露一个静态的公共方法,获取静态实例

代码实现

public class SingletonTest01 {

	public static void main(String[] args) {
		//测试
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance == instance2); // true
		System.out.println("instance.hashCode=" + instance.hashCode());
		System.out.println("instance2.hashCode=" + instance2.hashCode());
	}

}

//饿汉式(静态变量)

class Singleton {
	
	//1. 构造器私有化, 外部能new
	private Singleton() {
		
	}
	
	//2.本类内部创建对象实例
	private final static Singleton instance = new Singleton();
	
	//3. 提供一个公有的静态方法,返回实例对象
	public static Singleton getInstance() {
		return instance;
	}
	
}

优缺点说明

优点

  • 写法简单,在类装载的时候就完成实例化,防止了线程同步问题

缺点

  • 没有使用懒加载,一开始就会创建对象,如果从来没有使用过这个实例,就会造成内存浪费

2、饿汉式(静态代码块)

代码演示

public class SingletonTest02 {

	public static void main(String[] args) {
		//测试
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance == instance2); // true
		System.out.println("instance.hashCode=" + instance.hashCode());
		System.out.println("instance2.hashCode=" + instance2.hashCode());
	}

}

//饿汉式(静态变量)

class Singleton {
	
	//1. 构造器私有化, 外部能new
	private Singleton() {
		
	}
	

	//2.本类内部创建对象实例
	private  static Singleton instance;
	
	static { // 在静态代码块中,创建单例对象
		instance = new Singleton();
	}
	
	//3. 提供一个公有的静态方法,返回实例对象
	public static Singleton getInstance() {
		return instance;
	}
	
}

优缺点说明

优缺点:

  • 这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
  • 结论:这种单例模式可用,但是可能造成内存浪费

3、懒汉式(线程不安全)

代码演示

class Singleton {
	private static Singleton instance;
	
	private Singleton() {}
	
	//提供一个静态的公有方法,当使用到该方法时,才去创建 instance
	//即懒汉式
	public static Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

优缺点说明

  • 起到了Lazy Loading的效果,但是只能在单线程下使用。
  • 如果在多线程下,一个线程进入了if (singleton = mull)判断语句块, 还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
  • 结论:在实际开发中,不要使用这种方式

4、懒汉式(线程安全,同步方法)

代码演示


// 懒汉式(线程安全,同步方法)
class Singleton {
	private static Singleton instance;

	private Singleton() {}

	//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
	//即懒汉式
	public static synchronized Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

优缺点说明

  • 解决了线程安全问题
  • 效率太低了,每个线程在想获得类的实例时候,执行getInstance0方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低
  • 结论:在实际开发中,不推荐使用这种方式

 

5、懒汉式(线程安全,同步代码块)

代码演示

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

说明

  • 此种方法,并没有实现线程安全,因为如果当两个线程同时通过了第一个if,那么还是会存在线程安全问题,因此改进版是双重if,就是下面的方法。

6、双重检查

代码演示

// 懒汉式(线程安全,同步方法)
class Singleton {
	private static volatile Singleton instance;
	
	private Singleton() {}
	
	//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
	//同时保证了效率, 推荐使用
	
	public static synchronized Singleton getInstance() {
		if(instance == null) {
			synchronized (Singleton.class) {
				if(instance == null) {
					instance = new Singleton();
				}
			}
			
		}
		return instance;
	}
}

优缺点说明

  • 使用volatile关键字的目的:是为了防止指令重排。
  • Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton = mulI)检查,这样就可以保证线程安全了。
  • )这样,实例化代码只用执行- -次,后面再次访问时,判断if (singleton = mull), 直接return实例化对象,也避免的反复进行方法同步.
  • 线程安全:延迟加载:效率较高
  • 结论:在实际开发中,推荐使用这种单例设计模式

指令重排的解释 

在instance = new Singleton(); 中,并不是一个原子操作,在JVM中实际上它会完成以下的操作:

1.给instance分配内存

2.调用构造方法初始化成员变量

3.将instance指向分配的空间(此时instance就不为Null了)

由于不是原子操作,因此2/3可能会调换顺序。则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错

7、静态内部类

代码演示

// 静态内部类完成, 推荐使用
class Singleton {
	private static volatile Singleton instance;
	
	//构造器私有化
	private Singleton() {}
	
	//写一个静态内部类,该类中有一个静态属性 Singleton
	private static class SingletonInstance {
		private static final Singleton INSTANCE = new Singleton(); 
	}
	
	//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
	
	public static synchronized Singleton getInstance() {
		
		return SingletonInstance.INSTANCE;
	}
}

优缺点说明

保证线程安全的原因:

  • 采用了类装载机制保证,在初始化实例时只有一个线程,类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

实现懒加载的原因:

  • 由于静态内部类在Singleton类被装载时,并不会立即并初始化,而是在调用getInstance方法时,才会装载,实现懒加载

8、枚举

代码演示

//使用枚举,可以实现单例, 推荐
enum Singleton {
	INSTANCE; //属性
	public void sayOK() {
		System.out.println("ok~");
	}
}

优缺点说明

  1. 借助了JDK5.0中新增的枚举来实现单例模式,不仅避免了线程同步问题,也防止了反序列化重新创建对象的可能

三、单例模式在JDK中应用源码分析

java.lang.Runtime就是经典的单例模式(饿汉式)

四、单例模式注意事项和细节说明

  1. 单例模式保证了系统内存中该类只存在-一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
  2. 当想实例化一一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
  3. 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值