`
rainworlder
  • 浏览: 30399 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

lazy single

 
阅读更多

 

viclee

博观而约取 厚积而薄发

 
 
 
<iframe id="iframeu2392861_0" style="border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;" src="http://pos.baidu.com/zckm?sz=300x250&amp;rdid=2392861&amp;dc=2&amp;di=u2392861&amp;dri=0&amp;dis=0&amp;dai=1&amp;ps=1083x1892&amp;coa=at%3D3%26rsi0%3D300%26rsi1%3D250%26pat%3D17%26tn%3DbaiduCustNativeAD_xuanfu%26rss1%3D%2523FFFFFF%26conBW%3D1%26adp%3D1%26ptt%3D0%26titFF%3D%2525E5%2525BE%2525AE%2525E8%2525BD%2525AF%2525E9%25259B%252585%2525E9%2525BB%252591%26titFS%3D14%26rss2%3D%2523000000%26titSU%3D0&amp;dcb=BAIDU_SSP_define&amp;dtm=HTML_POST&amp;dvi=0.0&amp;dci=-1&amp;dpt=none&amp;tsr=0&amp;tpr=1476932448426&amp;ti=Java%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E2%80%94%E2%80%94%E5%B9%B6%E9%9D%9E%E7%9C%8B%E8%B5%B7%E6%9D%A5%E9%82%A3%E4%B9%88%E7%AE%80%E5%8D%95%20-%20viclee%20-%20%E5%8D%9A%E5%AE%A2%E9%A2%91%E9%81%93%20-%20CSDN.NET&amp;ari=2&amp;dbv=2&amp;drs=3&amp;pcs=1903x1094&amp;pss=1903x5714&amp;cfv=0&amp;cpl=5&amp;chi=1&amp;cce=true&amp;cec=UTF-8&amp;tlm=1476932448&amp;rw=1094&amp;ltu=http%3A%2F%2Fblog.csdn.net%2Fgoodlixueyong%2Farticle%2Fdetails%2F51935526&amp;ltr=http%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DrbxFhiF5BT3GqV3iFGllwQkiU3VtEkY3_6vum_cRPlLRIycrPyd481IBXnjUhL6CHg7Bd_ye7DRaeqY5daMa_f5GHHG7pCoNSyNuIbQ2pn_&amp;ecd=1&amp;psr=1920x1200&amp;par=1920x1160&amp;pis=-1x-1&amp;ccd=24&amp;cja=false&amp;cmi=7&amp;col=en-US&amp;cdo=-1&amp;tcn=1476932448&amp;qn=88c7b511186658dd&amp;tt=1476932448398.33.34.40" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" align="center,center" width="300" height="250"></iframe>
关闭
 

Java单例模式——并非看起来那么简单

标签: Java单例模式双重校验锁枚举
 2479人阅读 评论(1) 收藏 举报
 分类:
【Java进阶】(5)  【设计模式】(2) 

 

       Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在。一些管理器和控制器常被设计成单例模式。

       单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间;能够避免由于操作多个实例导致的逻辑错误。如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。

       单例模式有很多种写法,大部分写法都或多或少有一些不足。下面将分别对这几种写法进行介绍。

1、饿汉模式

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class Singleton{  
  2.     private static Singleton instance = new Singleton();  
  3.     private Singleton(){}  
  4.     public static Singleton newInstance(){  
  5.         return instance;  
  6.     }  
  7. }  
       从代码中我们看到,类的构造函数定义为private的,保证其他类不能实例化此类,然后提供了一个静态实例并返回给调用者。饿汉模式是最简单的一种实现方式,饿汉模式在类加载的时候就对实例进行创建,实例在整个程序周期都存在。它的好处是只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。它的缺点也很明显,即使这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。

 

       这种实现方式适合单例占用内存比较小,在初始化时就会被用到的情况。但是,如果单例占用的内存比较大,或单例只是在某个特定场景下才会用到,使用饿汉模式就不合适了,这时候就需要用到懒汉模式进行延迟加载。

2、懒汉模式

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class Singleton{  
  2.     private static Singleton instance = null;  
  3.     private Singleton(){}  
  4.     public static Singleton newInstance(){  
  5.         if(null == instance){  
  6.             instance = new Singleton();  
  7.         }  
  8.         return instance;  
  9.     }  
  10. }  

 

       懒汉模式中单例是在需要的时候才去创建的,如果单例已经创建,再次调用获取接口将不会重新创建新的对象,而是直接返回之前创建的对象。如果某个单例使用的次数少,并且创建单例消耗的资源较多,那么就需要实现单例的按需创建,这个时候使用懒汉模式就是一个不错的选择。但是这里的懒汉模式并没有考虑线程安全问题,在多个线程可能会并发调用它的getInstance()方法,导致创建多个实例,因此需要加锁解决线程同步问题,实现如下。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class Singleton{  
  2.     private static Singleton instance = null;  
  3.     private Singleton(){}  
  4.     public static synchronized Singleton newInstance(){  
  5.         if(null == instance){  
  6.             instance = new Singleton();  
  7.         }  
  8.         return instance;  
  9.     }  
  10. }  

 

3、双重校验锁

       加锁的懒汉模式看起来即解决了线程并发问题,又实现了延迟加载,然而它存在着性能问题,依然不够完美。synchronized修饰的同步方法比一般方法要慢很多,如果多次调用getInstance(),累积的性能损耗就比较大了。因此就有了双重校验锁,先看下它的实现代码。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class Singleton {  
  2.     private static Singleton instance = null;  
  3.     private Singleton(){}  
  4.     public static Singleton getInstance() {  
  5.         if (instance == null) {  
  6.             synchronized (Singleton.class) {  
  7.                 if (instance == null) {//2  
  8.                     instance = new Singleton();  
  9.                 }  
  10.             }  
  11.         }  
  12.         return instance;  
  13.     }  
  14. }  
       可以看到上面在同步代码块外多了一层instance为空的判断。由于单例对象只需要创建一次,如果后面再次调用getInstance()只需要直接返回单例对象。因此,大部分情况下,调用getInstance()都不会执行到同步代码块,从而提高了程序性能。不过还需要考虑一种情况,假如两个线程A、B,A执行了if (instance == null)语句,它会认为单例对象没有创建,此时线程切到B也执行了同样的语句,B也认为单例对象没有创建,然后两个线程依次执行同步代码块,并分别创建了一个单例对象。为了解决这个问题,还需要在同步代码块中增加if (instance == null)语句,也就是上面看到的代码2。

 

       我们看到双重校验锁即实现了延迟加载,又解决了线程并发问题,同时还解决了执行效率问题,是否真的就万无一失了呢?

       这里要提到Java中的指令重排优化。所谓指令重排优化是指在不改变原语义的情况下,通过调整指令的执行顺序让程序运行的更快。JVM中并没有规定编译器优化相关的内容,也就是说JVM可以自由的进行指令重排序的优化。

       这个问题的关键就在于由于指令重排优化的存在,导致初始化Singleton和将对象地址赋给instance字段的顺序是不确定的。在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。

       以上就是双重校验锁会失效的原因,不过还好在JDK1.5及之后版本增加了volatile关键字。volatile的一个语义是禁止指令重排序优化,也就保证了instance变量被赋值的时候对象已经是初始化过的,从而避免了上面说到的问题。代码如下:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class Singleton {  
  2.     private static volatile Singleton instance = null;  
  3.     private Singleton(){}  
  4.     public static Singleton getInstance() {  
  5.         if (instance == null) {  
  6.             synchronized (Singleton.class) {  
  7.                 if (instance == null) {  
  8.                     instance = new Singleton();  
  9.                 }  
  10.             }  
  11.         }  
  12.         return instance;  
  13.     }  
  14. }  

 

4、静态内部类

       除了上面的三种方式,还有另外一种实现单例的方式,通过静态内部类来实现。首先看一下它的实现代码:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class Singleton{  
  2.     private static class SingletonHolder{  
  3.         public static Singleton instance = new Singleton();  
  4.     }  
  5.     private Singleton(){}  
  6.     public static Singleton newInstance(){  
  7.         return SingletonHolder.instance;  
  8.     }  
  9. }  

 

       这种方式同样利用了类加载机制来保证只创建一个instance实例。它与饿汉模式一样,也是利用了类加载机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。

5、枚举

       再来看本文要介绍的最后一种实现方式:枚举。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public enum Singleton{  
  2.     instance;  
  3.     public void whateverMethod(){}      
  4. }  

 

       上面提到的四种实现单例的方式都有共同的缺点:

1)需要额外的工作来实现序列化,否则每次反序列化一个序列化的对象时都会创建一个新的实例。

2)可以使用反射强行调用私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。

       而枚举类很好的解决了这两个问题,使用枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,《Effective Java》作者推荐使用的方法。不过,在实际工作中,很少看见有人这么写。

 

 

总结

       本文总结了五种Java中实现单例的方法,其中前两种都不够完美,双重校验锁和静态内部类的方式可以解决大部分问题,平时工作中使用的最多的也是这两种方式。枚举方式虽然很完美的解决了各种问题,但是这种写法多少让人感觉有些生疏。个人的建议是,在没有特殊需求的情况下,使用第三种和第四种方式实现单例模式。

 

参考文章:http://www.jfox.info/java-dan-li-mo-shi-de-ji-zhong-xie-fa

http://devbean.blog.51cto.com/448512/203501/

 
4
0
 

我的同类文章

【Java进阶】(5) 【设计模式】(2)
 

参考知识库

img

Java EE知识库

7347关注|705收录

img

Java SE知识库

14406关注|459收录

img

Java 知识库

16429关注|1321收录

猜你在找
<iframe id="aswift_0" style="left: 0px; position: absolute; top: 0px;" name="aswift_0" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="728" height="90"></iframe>
查看评论
1楼 android_cyw 2016-08-25 14:15发表 [回复]
指令重排优化,之前一直没注意这个问题,赞!
 
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
 
 
 
 
    个人资料
 
 
    • 访问:446477次
    • 积分:4910
    • 等级: 
    • 排名:第4127名
    • 原创:85篇
    • 转载:49篇
    • 译文:1篇
    • 评论:98条
    文章存档
    最新评论
 
<iframe id="iframeu2734128_0" style="border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;" src="http://pos.baidu.com/zckm?sz=200x200&amp;rdid=2734128&amp;dc=2&amp;di=u2734128&amp;dri=0&amp;dis=0&amp;dai=2&amp;ps=2968x471&amp;coa=at%3D3%26rsi0%3D200%26rsi1%3D200%26pat%3D6%26tn%3DbaiduCustNativeAD%26rss1%3D%2523FFFFFF%26conBW%3D1%26adp%3D1%26ptt%3D0%26titFF%3D%2525E5%2525BE%2525AE%2525E8%2525BD%2525AF%2525E9%25259B%252585%2525E9%2525BB%252591%26titFS%3D%26rss2%3D%2523000000%26titSU%3D0%26ptbg%3D90%26piw%3D0%26pih%3D0%26ptp%3D0&amp;dcb=BAIDU_SSP_define&amp;dtm=HTML_POST&amp;dvi=0.0&amp;dci=-1&amp;dpt=none&amp;tsr=0&amp;tpr=1476932448426&amp;ti=Java%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E2%80%94%E2%80%94%E5%B9%B6%E9%9D%9E%E7%9C%8B%E8%B5%B7%E6%9D%A5%E9%82%A3%E4%B9%88%E7%AE%80%E5%8D%95%20-%20viclee%20-%20%E5%8D%9A%E5%AE%A2%E9%A2%91%E9%81%93%20-%20CSDN.NET&amp;ari=2&amp;dbv=2&amp;drs=3&amp;pcs=1903x1094&amp;pss=1903x5714&amp;cfv=0&amp;cpl=5&amp;chi=1&amp;cce=true&amp;cec=UTF-8&amp;tlm=1476932448&amp;rw=1094&amp;ltu=http%3A%2F%2Fblog.csdn.net%2Fgoodlixueyong%2Farticle%2Fdetails%2F51935526&amp;ltr=http%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DrbxFhiF5BT3GqV3iFGllwQkiU3VtEkY3_6vum_cRPlLRIycrPyd481IBXnjUhL6CHg7Bd_ye7DRaeqY5daMa_f5GHHG7pCoNSyNuIbQ2pn_&amp;ecd=1&amp;psr=1920x1200&amp;par=1920x1160&amp;pis=-1x-1&amp;ccd=24&amp;cja=false&amp;cmi=7&amp;col=en-US&amp;cdo=-1&amp;tcn=1476932448&amp;qn=198a65bdd30c1ffb&amp;tt=1476932448398.49.49.347" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" align="center,center" width="200" height="200"></iframe>
 
 
分享到:
评论

相关推荐

    Multi-Label Lazy Associative Classification

    a set of instances that are associated with a single label (i.e., single-label classi- fication). However, many applications, such as gene functional prediction and text categorization, may allow the ...

    lazyload:zepto-lazyload

    Zepto-Lazyload是基于轻量级JavaScript库Zepto的一个插件,它实现了延迟加载功能,特别适合用于单页应用(SPA,Single-Page Application)。Zepto本身是对jQuery的一个精简版本,针对移动设备做了优化,而Zepto-Lazy...

    Single Page Web Applications

    开发者需要了解如何使用代码分割(code splitting)、懒加载(lazy loading)、和服务工作线程(service workers)等技术来提高应用的加载速度和响应速度。 7. **安全性**:安全性在Web开发中始终是一个重要话题,...

    hibernate在单端关联上的lazy策略

    在压缩包文件“hibernate_lazy_for_single_end”中,可能包含了一些示例代码或测试用例,帮助读者理解并实践Hibernate的单端关联懒加载策略。这些文件可能包括实体类、映射文件、测试类等,通过实际操作展示如何配置...

    hibernate-html-single

    13. **懒加载(Lazy Loading)**: 为了提高性能,Hibernate允许延迟加载关联的对象,直到真正需要时才从数据库中获取。 14. **事件监听器(Event Listeners)**: Hibernate允许注册监听器来处理特定的持久化事件,...

    Single Page Web Application JavaScript end to end

    单页Web应用程序(Single Page Web Application,简称SPA)是一种特殊的Web应用程序,它提供与桌面应用程序相似的用户体验,通过只加载一个HTML页面并在用户与应用程序交互时动态更新此页面内容来实现。SPA能够与...

    jetpack-lazy-images:[仅供阅读]通过在访客向下滚动屏幕时而不是一次全部滚动来加载图像,可以加快您的网站访问速度并创造更流畅的观看体验。 该存储库是一个镜像,可用于问题跟踪和开发

    Jetpack懒惰图像 通过在访问者向下滚动屏幕时而不是... * on single posts, pages, or attachments by short-circuiting lazy images when * is_singular() returns false. * * See: https://github.com/Automattic/

    single-page-app

    单页应用(Single-Page Application,SPA)是现代Web开发中的一个重要概念,它与传统的多页面网站形成鲜明对比。在SPA中,整个应用程序通常只加载一个HTML页面,然后通过JavaScript动态更新页面内容,无需每次都完全...

    single-page-sites:只是一些测试

    这可能涉及到懒加载(lazy loading)、预加载(prefetching)策略,以及对首屏内容的优化,确保页面首次加载速度快。在这些测试中,可能会涉及这些优化技术的应用。 总的来说,这个"single-page-sites"项目提供了一...

    Java单例类

    在上面的代码中,我们使用了synchronized关键字来保证线程安全,并且使用lazy loading机制来实例化对象。 注册式单例 注册式单例是一种可以继承的单例模式实现方式。其特点是:可以继承注册式单例类,并且可以在...

    过滤器对于session周期的控制

    **Lazy Loading**是一种延迟加载策略,即在真正需要数据时才进行加载。这种策略能够显著减少应用程序启动时的内存占用,并提高系统的响应速度。然而,这也意味着如果Session在加载懒加载属性之前已经关闭,则会导致`...

    -O-:Just a simple static blog Generator with single static pages. 通过GithubAPI交互实现在浏览器中发布文章的静态博客系统

    Just a simple static blog Generator with single static pages. 现在开始就在GithubPages上撰写吧 想法:thinking_face: 之前逛v2社区的时候看到有人写了一个利用github api的博客,顿时非常心动,但项目已经久未...

    angularjs懒加载

    AngularJS 懒加载(Lazy Loading)是一种优化前端应用性能的技术,主要应用于单页应用程序(SPA,Single Page Application)。在传统的 AngularJS 应用中,所有模块和控制器都会在初始加载时一并加载到浏览器中,这...

    23种经典设计模式-C#版本

    除了上述两种方式,还有其他实现单例模式的方法,如`Lazy&lt;T&gt;`类(自.NET Framework 4.0起)提供的延迟初始化单例,以及使用`密封`(`sealed`)和静态构造函数的双检锁(Double-Check Locking)实现。每种方法都有其...

    HTML5-Single-Page:HTML5单页

    HTML5单页应用(Single-Page Application,SPA)是一种现代Web开发模式,它利用HTML5、CSS3和JavaScript等技术,构建用户仅需一次加载就能访问的网页,提供类似桌面应用的流畅体验。SPA通过动态地在后台更新内容,而...

    hibernate_annotations API中文

    Hibernate支持单表继承(Single Table Inheritance)、联合继承( Joined Table Inheritance)和表格分区继承(Table Per Concrete Class)。例如,使用`@Inheritance(strategy=InheritanceType.SINGLE_TABLE)`实现...

    23种设计模式

    除了上述两种实现方式,还有其他方法,比如使用`Lazy&lt;T&gt;`类或者.NET框架提供的`System.Threading.Concurrent`命名空间中的`ConcurrentDictionary`。这些方法都提供了线程安全的单例实现,但具体选择哪种方式取决于...

    java 中设计模式之单例

    单例模式通常有两种常见的实现方式:饿汉式(Eager Initialization)和懒汉式(Lazy Initialization)。 1. **饿汉式**: 饿汉式单例的特点是类加载时立即初始化,因此也称为预加载单例。这种方式简单且线程安全,...

    Simple Machine Learning for Programmers

    It means that instead of having to understand every single line of code in great detail, you instead trust that other programmers, all building their respective components, have completed their ...

Global site tag (gtag.js) - Google Analytics