vlambda博客
学习文章列表

单例模式之懒汉模式&饿汉模式

1. 概述

单例模式属于创建型模式的一种,应用于保证一个类仅有一个实例的场景下,并且提供了一个访问它的全局访问点,如spring中的全局访问点BeanFactory,spring下所有的bean都是单例。单例模式的特点:从系统启动到终止,整个过程只会产生一个实例。

想必大家对上述的概念应该是耳熟能详了,但单例模式又细分为懒汉式,饿汉式,注册式,序列化式。下面比较一下懒汉式和饿汉式:

2. 懒汉模式

  • 顾名思义,懒汉模式该只在你需要对象时才会生成单例对象,默认不会实例化,什么时候用什么时候new。

  • 示例代码

 
   
   
 
  1. public class User {

  2. //懒汉式单例,只有在调用getInstance时才会实例化一个单例对象

  3. public static User user;

  4. private User(){


  5. }


  6. public static User getInstance(){

  7. if(user==null){ //step 1.

  8. user = new User(); //step 2

  9. }

  10. return user;

  11. }

  12. }

2.1. 优缺点

  • 优点:节约内存,对象只在有用的时候开辟

  • 缺点:非线程安全。假设当前有N个线程同时调用getInstance()方法,由于当前还没有对象生成,所以一部分同时都进入step 2,那么就会由多个线程创建多个多个user对象。

  • 解决办法:使用双重锁定的方式解决线程安全问题

 
   
   
 
  1. public class User {

  2. //懒汉式单例,只有在调用getInstance时才会实例化一个单例对象

  3. public static User user;


  4. public static Integer key = new Integer(4); //作为一个锁

  5. private User(){


  6. }


  7. public static User getInstance(){

  8. //先判断该user变量是否为空,入股为空,进入同步代码块,该步假设为step1

  9. if(user == null){ //step 1

  10. /*想象一下,如果不判断,那么每次访问这个方法不管该对象是否已经创建都要进入同步代码块,线程数一多,资源消耗也是非常巨大的。*/

  11. synchronized (key) {

  12. /*由于可能多个线程都进入了step1,由于锁定机制,一个线程进入该代码块时,其他线程仍在排队进入该代码块,如果不做判断,当前线程即使创造了实例,下一个线程也不知道,就会继续创建一个实例*/

  13. if(user==null){

  14. user = new User();

  15. }

  16. }

  17. }

  18. return user;

  19. }

  20. }

3. 饿汉模式

  • 定义:提前占用系统资源 调用得到实例方法的时候内部类才会加载,这样就延缓了加载时机,提高了程序运行的效率

 
   
   
 
  1. public class Person {

  2. //饿汉式单例

  3. private static Person person = new Person();

  4. private Person(){}


  5. public static Person getInstance(){

  6. return person;

  7. }

  8. }

4. 二者对比

  • 执行效率上:饿汉式没有加任何的锁,因此执行效率比较高。懒汉式一般使用都会加同步锁,效率比饿汉式差。

  • 性能上:饿汉式在类加载的时候就初始化,不管你是否使用,它都实例化了,所以会占据空间,浪费内存。懒汉式什么时候需要什么时候实例化,相对来说不浪费内存。