设计模式---------单例模式

java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍两种:懒汉式单例、饿汉式单例。

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

单例模式四大原则:
1.构造私有。
2.以静态方法或者枚举返回实例。
3.确保实例只有一个,尤其是多线程环境。
4.确保反序列换时不会重新构建对象。

线程安全的饿汉模式

public class Hungry {

	/**
	 * @param args
	 */
	//内部实例化
	private final static Hungry hungry=new Hungry();
	
	//私有构造方法
	private Hungry(){
		
	}
	//对外提供调用实例
	public Hungry getHungry(){
		return hungry;
	}
}

饿汉模式是线程安全的。饿汉模式的对象在类产生时候就创建了,一直到程序结束才会去释放。即作为一个单例类实例,它的生存周期和我们的程序一样长。因此该实例对象需要存储在全局数据区,所以肯定需要使用static来修饰,因为类内部的static成员是不属于每个对象的,而是属于整个类的。在加载类的时候,我们的实例对象就产生了。但是他是在加载类的时候创建实例,如果缓存了很多实例,就会影响效率问题,所以说这种加载无法实现懒加载。

懒汉模式
为了缓解在类加载是即完成实例,实现懒加载,便有了懒汉模式

public class Lazy {

		private static Lazy lazy=null;
		
		//私有构造方法
		private Lazy(){	
		}
		
		//需要时完成加载
		public static Lazy getLazy(){
			if(lazy==null){
				lazy=new Lazy();
			}
			return lazy;
		}
		
}

但是如果是在多线程并发的 情况下,这种方法是线程不安全的无法保证一个实例。所以我们加synchronized

public class Lazy {

		private static Lazy lazy=null;
		
		//私有构造方法
		private Lazy(){	
		}
		
		//需要时完成加载是线程安全的
		public static synchronized Lazy getLazy(){
			if(lazy==null){
				lazy=new Lazy();
			}
			return lazy;
		}
		
}

然而并发其实是一种特殊情况,大多时候这个锁占用的额外资源都浪费了,这种方式写出来的结构效率很低。
所以改进成为了双重校验锁法
双重锁懒汉模式(Double Check Lock)

public class Lazy {

		private static Lazy lazy=null;
		
		//私有构造方法
		private Lazy(){	
		}
		
		//双重校验锁法
		public static  Lazy getLazy(){
			if(lazy==null){
				synchronized(Lazy.class){
					if(lazy==null){
						lazy=new Lazy();
					}
				}
			}
			return lazy;
		}
}

DCL模式的优点就是,只有在对象需要被使用时才创建,第一次判断 INSTANCE == null为了避免非必要加锁,当第一次加载时才对实例进行加锁再实例化。这样既可以节约内存空间,又可以保证线程安全。但是,由于jvm存在乱序执行功能,DCL也会出现线程不安全的情况。具体分析如下:
INSTANCE = new SingleTon();
这个步骤,其实在jvm里面的执行分为三步:
1.在堆内存开辟内存空间。
2.在堆内存中实例化SingleTon里面的各个参数。
3.把对象指向堆内存空间。

由于jvm存在乱序执行功能,所以可能在2还没执行时就先执行了3,如果此时再被切换到线程B上,由于执行了3,INSTANCE 已经非空了,会被直接拿出来用,这样的话,就会出现异常。这个就是著名的DCL失效问题。

不过在JDK1.5之后,官方也发现了这个问题,故而具体化了volatile,即在JDK1.6及以后,只要定义为private volatile static SingleTon INSTANCE = null;就可解决DCL失效问题。volatile确保INSTANCE每次均在主内存中读取,这样虽然会牺牲一点效率,但也无伤大雅

登记式/静态内部类
这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。饿汉模式类加载便一定会实例化。但是使用静态内部类类加载是不一定会实例化。因为 SingletonHolder 类没有被主动使用,只有显示通过调用 getInstance 方法时,才会显示装载 SingletonHolder 类,从而实例化 instance。

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

枚举
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。但它无法实现懒加载。

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}  



public static void main(String[] args) {
		Singleton.INSTANCE.whateverMethod() ;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值