Java必懂之单例模式

一、背景知识

在所有的主流23中设计模式中,单例模式作为最简单也最容易理解也是使用最普遍的设计模式,他是一种对象创建模式,用于产生一个对象的具体实例,他可以确保系统中一个类只产生一个实例。


单例模式的优点如下:

1、对于频繁创建的对象,可以省去new的操作锁花费的时间,对于一些重量级的对象创建来说可以说是一笔开销的节省;

2、由于new的次数变少了,系统内存的使用频率也会降低,这也减轻了GC的压力,缩短了GC停顿的时间,这是因为GC收集的时候去阻塞程序的执行。


单利模式的七种写法主要是:懒汉模式线程不安全、懒汉模式线程安全、饿汉模式、变种的饿汉模式、双重校验锁DCL、静态内部类、枚举类型。

下边主要分析一下:饿汉模式、懒汉模式线程安全、双重校验锁DCL、静态内部类四种,其他几种大致类似,不在赘述。

二、饿汉模式

/**
 * 饿汉单例模式
 * @author 小跳蛙
 *
 */
public class Singleton {

	//1、实例化对象必须是private,并且是static,是private是可以保证安全性
	private static Singleton  singleton = new Singleton();
	//2、构造方法必须是私有化的,这样可以避免其他任意调用的情况
	private Singleton() {
		
	}
	//3、提供方法以供外部获取实例对象,此方法必须为public static, public保证外部有权限能访问,
			//static保证不需要先实例化Singleton对象就能访问此方法
	public static Singleton getInstance() {
		return singleton;
	}
}

三、懒汉模式

/**
 * 懒汉模式线程安全
 * @author 刘威辰的秘密花园
 *
 */
public class LazySingeton {
	//声明
	private static LazySingeton instance;
	
	//私有化构造器
	private LazySingeton() {
		
	}
	
	public synchronized LazySingeton getInstance() {
		if(instance == null) {
			instance = new LazySingeton();
		}
		return instance;
	}
	
}


四、双重校验锁DCL

但是在多线程并发访问的情况下,每个线程每次获取实例都要判断下锁,效率比较低。为了提高效率,我加入了双重判断的方法,解决了效率的问题

public class SingleDemo {  
    private volatile static SingleDemo s = null;  
    private SingleDemo(){}  
    public static  SingleDemo getInstance(){  
        /*如果第一个线程获取到了单例的实例对象, 
         * 后面的线程再获取实例的时候不需要进入同步代码块中了*/  
        if(s == null){  
            //同步代码块用的锁是单例的字节码文件对象,且只能用这个锁  
            synchronized(SingleDemo.class){  
                if(s == null){  
                    s = new SingleDemo();  
                }  
            }  
        }  
        return s;  
    }  
}  

(相对的,就是把synchronized锁去掉,在这里就省略这段代码了)


五、静态内部类

/**
 * 静态内部类
 * @author 小跳蛙
 *
 */
public class StaticSingleton {

	private static class StaticSingletonHolder{
		private static final StaticSingleton instance = new StaticSingleton();
	}
	private StaticSingleton() {
		
	}
	
	public static final StaticSingleton getInstance() {
		return StaticSingletonHolder.instance;
	}
}
简单分析一下:

1、可以实现延迟加载的功能,只有在调用getInstance()的方法才会创建单例对象,并且是通过类加载器机制保证只创建一个单例对象;

2、对于java类加载机制来说,当第一次访问类的静态字段的时候,会触发类加载,并且同一个类只加载一次。静态内部类也是如此,只会被加载一次,类加载过程由类加载器负责加锁,从而保证线程安全。

3、代码简单明了,值得信赖


六、总结:

第一种:饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。

第二种:就是懒汉模式线程安全,在单线程中一般可以保证线程安全,但是在多线程中,有可能会出现不同步,于是提出第三种。

第三种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗,但是这种方法代码量多,并且难以理解,还不能保证一些低版本JDK执行的正确性,所以也不推荐使用。

第四种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。


大家也可以去http://mp.weixin.qq.com/s/3KATGzc-IIaP7lQOFJp6Ew看看,这是我对这篇文章及网上一些相关文章作出的总结,谢谢大家!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值