java中你确定用对单例了吗?
2017-02-24 21:41
141 查看
作为程序猿这种特殊物种来说,都掌握了一种特殊能力就是编程思想,逻辑比较谨慎,但是有时候总会忽略到一些细节,比如我,一直以来总觉得
好不扯淡了,直入主题.
在代码的世界里发现有各种写法的单例,有人说单例有5种,6种,7种…
对于单例的分类这点必须规范下,首先这么多种的分类是根据什么来定义的,基准是什么?否则怎么会有那么多写法.
因此归纳下来,从
没错上面这块代码叫做
>
开发评价: ★★★★☆
延时加载: ★☆☆☆☆
执行效率: ★★★★★
在
这个方式主要是通过if判断非null实例,提高了执行的效率,不必每次
但是为什么说这种写法有bug?这个问题主要是java的jvm层内部模型引起的.简单点说就是
详情见
>
开发评价: ★★★☆☆
延时加载: ★★★☆☆
执行效率: ★★★☆☆
这周方式是利用了类加载的一些特性,在classloder的机制中,初始化instance时只有一个线程,而且这种方式还有一个好处就是起到了延时加载的效果,当
因此这种巧妙的方式比起双重检查锁来说,安全来又高效了些.
>
开发评价: ★★★★☆
延时加载: ★★★★☆
执行效率: ★★★★☆
这种方式其实很帅,但是在实际开发中很少人使用过,这点有点奇怪,首先enum本身就是一个单例,而且枚举还有一个特性,可以避免放序列化和反射破解单例问题,经理再也不用担心单例安全了,可能是1.5才有enum的原因吧,如果项目适合的话可以试下这种单例.
>
开发评价: ★★★★☆
延时加载: ★★★★☆
执行效率: ★★★★☆
饿汉
标准饿汉 (安全防护方面 枚举单例更优于标准饿汉)
线程安全,高效,不可以懒加载
枚举单例
线程安全,高效,不可以懒加载(天然避免反射与反序列化)
懒汉 (效率方面 静态内部类更优于标准懒汉)
标准懒汉
线程安全,低效,可以懒加载
双重检测(不推荐,有bug)
线程安全,低效,可以懒加载
静态内部类
线程安全,低效,可以懒加载
对于单例的安全性问题,可以继续你那帅气的阅读姿势, java中你的单例是不是一直在裸奔
Singleton是设计模式里最简单的,不用太在意,然而就是因为这种不在意在开发中吃亏了.真的too young to simple.
好不扯淡了,直入主题.
在代码的世界里发现有各种写法的单例,有人说单例有5种,6种,7种…
对于单例的分类这点必须规范下,首先这么多种的分类是根据什么来定义的,基准是什么?否则怎么会有那么多写法.
因此归纳下来,从
延迟加载和
执行效率的角度出发主要也就分为两种,饿汉顾名思义就是执行效率高,但缺乏延时加载,其他写法差不多都是懒汉式的一个拓展,或者优化而演化出来的,下面请看代码.
开发中常用的单例-饿汉式
public class SingletonDemo1 { private static final SingletonDemo1 s1 = new SingletonDemo1(); public static SingletonDemo1 getInstance() { return s1; } private SingletonDemo1() { } }
没错上面这块代码叫做
单例-饿汉式,饿汉式一直以效率高而闻名于单例界,因此咋们开发中常用的单例模式也会选择他,简单而好用.
>
开发评价: ★★★★☆
延时加载: ★☆☆☆☆
执行效率: ★★★★★
耗时的蜗牛-懒汉式
public class SingletonDemo2 { private static SingletonDemo2 s1; public static synchronized SingletonDemo2 getInstance() { if (s1 == null) { s1 = new SingletonDemo2(); } return s1; } private SingletonDemo2() { } }
在
hello world这个世界里都知道这种单例基本不会去用,在
double check双重检查锁-懒汉式
这可以说是上面饿汉式的一个缩影,为什么这么说,因为他并不完美,仍然有bug.public class SingletonDemo3 { private static SingletonDemo3 s1; public static SingletonDemo3 getInstance() { if (s1 == null) { //这里使用了临时变量 SingletonDemo3 instance; synchronized (SingletonDemo3.class) { instance = s1; if (instance == null) { synchronized (SingletonDemo3.class) { if (instance == null) { instance = new SingletonDemo3(); } } s1 = instance; } } } return s1; } }
这个方式主要是通过if判断非null实例,提高了执行的效率,不必每次
getInstace都要进行synchronize,只要第一次要同步,有没创建了不用.
但是为什么说这种写法有bug?这个问题主要是java的jvm层内部模型引起的.简单点说就是
instance引用的对象有可能还没有完成初始化完就直接返回该实例过去,在jdk1.5后这个问题才得到了优化,这不多说,可以看看这篇博文讲得不错.
详情见
当然也有了一些解决方法
使用volatile关键字解决双重检查锁定的bug,对于volatile关键字就是Java中提供的另一种解决可见性和有序性问题的方案.public class SafeDoubleCheckedLocking { //添加了volatile关键字 private volatile static Instance instance; public static Instance getInstance() { if (instance == null) { synchronized (SafeDoubleCheckedLocking.class) { if (instance == null) instance = new Instance();//instance为volatile,现在没问题了 } } return instance; } }
>
开发评价: ★★★☆☆
延时加载: ★★★☆☆
执行效率: ★★★☆☆
推荐使用的静态内部类-懒汉式
public class SingletonDemo4 { //通过静态内部类的方式来实例化对象 private static class InnerSingleton { private static final SingletonDemo4 instance = new SingletonDemo4(); } public static SingletonDemo4 getInstance() { return InnerSingleton.instance; } private SingletonDemo4() { } }
这周方式是利用了类加载的一些特性,在classloder的机制中,初始化instance时只有一个线程,而且这种方式还有一个好处就是起到了延时加载的效果,当
SingletonDemo4被加载了,但是内部类
InnerSingleton并不会被加载,因为
InnerSingleton没有主动使用,只有通过调用
getInstance方法时才会去加载
InnerSingleton类,进而实例
private static final SingletonDemo4 instance = new SingletonDemo4();
因此这种巧妙的方式比起双重检查锁来说,安全来又高效了些.
>
开发评价: ★★★★☆
延时加载: ★★★★☆
执行效率: ★★★★☆
推荐使用的枚举-饿汉式
public enum SingletonDemo5 { //枚举元素本身就是一个单例(名字可以随意定义); INSTANCE; //可以做一些单例的初始化操作 public void singletonOperation() { } }
这种方式其实很帅,但是在实际开发中很少人使用过,这点有点奇怪,首先enum本身就是一个单例,而且枚举还有一个特性,可以避免放序列化和反射破解单例问题,经理再也不用担心单例安全了,可能是1.5才有enum的原因吧,如果项目适合的话可以试下这种单例.
>
开发评价: ★★★★☆
延时加载: ★★★★☆
执行效率: ★★★★☆
总结一下:
对于下面的单例总的来说各有各的优点,至于开发中使用哪个可以根据你的业务需求来选择.饿汉
标准饿汉 (安全防护方面 枚举单例更优于标准饿汉)
线程安全,高效,不可以懒加载
枚举单例
线程安全,高效,不可以懒加载(天然避免反射与反序列化)
懒汉 (效率方面 静态内部类更优于标准懒汉)
标准懒汉
线程安全,低效,可以懒加载
双重检测(不推荐,有bug)
线程安全,低效,可以懒加载
静态内部类
线程安全,低效,可以懒加载
对于单例的安全性问题,可以继续你那帅气的阅读姿势, java中你的单例是不是一直在裸奔
相关文章推荐
- 在Java中如何确定浮点数的精度,例如按要求确定小数点后面的数字个数
- 在Java中如何确定浮点数的精度,例如按要求确定小数点后面的数字个数
- Java运行期间动态确定调用方法的本质
- 【通用语法】C++ c# JAVA double 求指定有效位数 4舍5入,确定一个数组的整数上下限
- Java-确定字符串是否包含子字符串
- 使用eclipse JDT compile class,解决 没法确定 X 的类型参数;对于上限为 X,java.lang.Object 的类型变量 X,不存在唯一最大实例
- 关于在java中弹出确定取消的对话框问题
- 使用eclipse JDT compile class,解决 无法确定 X 的类型参数;对于上限为 X,java.lang.Object 的类型变量 X,不存在唯一最大实例
- 【java performance】确定StringBuffer的容量
- Java脚本提示用户是否确定操作。否则取消当前操作。
- 在java方法参数位置调用void方法,确定JAVA中void方法被成功调用--反射调用
- 关于在java中弹出确定取消的对话框问题
- JAVA中如何随机生成确定范围中的随机数
- 确定hive shell的java进程的pid的一种方法
- 在Java中确定浮点数的精度
- 利用Java脚本实现弹出窗口后,按确定实现跳转
- java中已知WEEK_OF_YEAR,确定当前周的周一和周日
- 系统分析---入门(如何确定java应用的入口)
- Java-确定一个类对象代表一个类还是接口
- Java IO 使用时具体对象的确定