单例模式(Singleton)

 

    单例对象(Singleton)是一种常用的设计模式。单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
    1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
    2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。

    单例模式的特点:
         1、只能有一个实例;
         2、必须自己创建自己的一个实例;
         3、必须给所有其他对象提供这一实例。

 

 

    饿汉式单例模式,也称为预先加载法,实现方式如下:

 

	class Singleton {  
		private static Singleton  instance = new Singleton();
		
		private Singleton() {  
		}  

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

 

 

    在类加载的同时已经创建好了一个静态对象(创建的唯一对象), 但是这种方式资源利用效率不高,可能该实例并不需要,但也被系统加载了。另外,饿汉式在一些场景下是无法使用的,比如,如果Single实例的创建依赖参数或配置文件,则在getInstance()之前必须调用某个方法来设置这些参数,但在设置之前,可能已经new了Single实例,这种情况下,饿汉式的写法是无法使用的。

 

 

    懒汉式单例模式,也称为延迟加载法,实现方式如下:

 

public class Singleton {  
	private static Singleton instance = null;  
	/* 私有构造方法,防止被实例化 */  
	private Singleton() {  
	} 
	
	public static Singleton getInstance() {  
		if (instance == null) {  
			instance = new Singleton();  
		}  
		return instance;  
	}  
}  

 

 

    这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?可以改成下面这个:

 

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

    将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间, 然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,以A、B两个线程为例:
    a> A、B线程同时进入了第一个if判断,A判断为null,B还未判断
    b> A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
    c>  由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例);
    d> B此时才判断,发现instance 此时不是null,将结果返回给调用该方法的程序。
    e> 此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
    所以程序还是有可能发生错误,其实程序在运行过程是很复杂的,从这点我们就可以看出,尤其是在写多线程环境下的程序更有难度,有挑战性。我们对该程序做进一步优化:

	class Singleton {  
		private Singleton(){
		} 
	      
		private static class InstanceHolder{  
			private static final Singleton instance = new Singleton();  
		}  
 
		public static Singleton getInstance(){  
			return InstanceHolder.instance;  
		}  
	}

    使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用 getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。

    也有这样实现:因为只需要在getInstance()加 synchronized关键字,或则用volatile修饰instance也是可以的。

补充:看了下另一个代码,感觉解决了分配给Singleton实例的空白内存,并赋值给instance,而JVM没有开始初始化这个实例,所导致的问题。上代码:

 

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

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值