转发自:http://blog.csdn.net/ligang7560/article/details/50890282
单例模式的多种实现方式
我们都知道单例模式有几种常用的写法:
- 饿汉模式
- 懒汉模式
- 双重校验锁
- 静态内部类
- 静态代码块
我们来看一下这几种模式在多线程的场景中,能否保持单例
1.饿汉模式
public class HungrySingleton {
//立即加载模式
private static HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
接下来我们写一个线程类,再写一个测试类,来看下饿汉模式在多线程下的表现如何:
运行测试类结果如下:
546862187
546862187
546862187
这说明饿汉模式是线程安全,在多线程的情况下,也能够保持单例.
2.懒汉模式
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton() {}
public static LazySingleton getInstance(){
if(lazySingleton!=null){
}else{
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
同样的我们来写一个线程类,写一个测试类;
运行结果:
2030251396
546862187
2030251396
我们看到对象返回的哈希值不一致了,这就说明他们已经不是同一个对象实例了,也就是说这种写法是线程不安全的,那么我们对它进行如下的改造:
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton() {}
//整个方法加同步,效率低
synchronized public static LazySingleton getInstance(){
if(lazySingleton!=null){
}else{
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
我们给整个getInstance方法添加了个synchronized关键字,才保证多线程的情况下也能保持单例,我们再运行一下测试类:
1638084561
1638084561
1638084561
这时我们看见已经没有问题了,就是说写法是正确的,但是这种写法导致所以并发的线程获取实例时都是排队进行的,那么就会导致性能低下,接下来我们尝试另外一种写法,就是我们的第三种写法:双重校验锁.
3. 双重校验锁
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton() {}
public static LazySingleton getInstance(){
if(lazySingleton!=null){
}else{
synchronized (LazySingleton.class){
if(lazySingleton==null){
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
}
我们将排队进行的范围进行缩小,同时采用校验两次为空的方法,来保证单例模式的正确性,接下来我们来看一下测试结果:
2030251396
2030251396
2030251396
OK,没有问题.
4.静态内部类
public class StaticInnerClassSingleton {
//通过写一个静态的内部类来创建实例
private static class SingletonHandle{
private static StaticInnerClassSingleton singleton = new StaticInnerClassSingleton();
}
private StaticInnerClassSingleton(){}
public static StaticInnerClassSingleton getInstance(){
return SingletonHandle.singleton;
}
}
测试类和上面的都差不多,我就不贴图片了,直接看测试结果:
1523917841
1523917841
1523917841
没有问题,这种方式也是线程安全的,接下来我们看最后一种方式
5.静态代码块
public class StaticSingleton {
private static StaticSingleton staticSingleton=null;
private StaticSingleton(){};
static {
//通过静态代码块的执行,来获取实例
staticSingleton = new StaticSingleton();
}
public static StaticSingleton getInstance(){
return staticSingleton;
}
}
不废话了,直接看测试结果:
2030251396
2030251396
2030251396
没有问题,那么以上我们就介绍了5中关于多线程中单例模式的写法,其中呢就懒汉模式的一般写法容易造成错误,其他的写法都是线程安全的,文章中的代码我已经上传到github了,有需要的同学可以下载下来自己试试.
https://github.com/JokerLigang/Singleton.git