快上网专注成都网站设计 成都网站制作 成都网站建设
成都网站建设公司服务热线:028-86922220

网站建设知识

十年网站开发经验 + 多家企业客户 + 靠谱的建站团队

量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决

java单例模式实现的方法是什么

这篇文章主要讲解了“java单例模式实现的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java单例模式实现的方法是什么”吧!

柳城网站建设公司创新互联,柳城网站设计制作,有大型网站制作公司丰富经验。已为柳城1000多家提供企业网站建设服务。企业网站搭建\外贸网站制作要多少钱,请找那个售后服务好的柳城做网站的公司定做!

许多人都了解,单例的创建大致可以分为两种:

  • 饱汉型

  • 饿汉型

所谓饿汉型,就是单例的对象,我上来就先创建好了,什么时候用直接拿就好了。例如这样的方式:

private static final Singleton singleton = new Singleton();
 
public static Singleton getInstance() {
   return singleton;
}

这样的实现方式没有任何问题。问题出在哪呢?

有些时候,要实现Singleton的对象比较大,或者创建比较耗资源,耗时等,我们希望能在需要的时候再初始化,而不放在class 加载的时候,也就是实现所谓的lazy load。

这个时候问题就来了,这种所谓的饱汉型要怎么写呢?

你可能见到过这种形式的实现

private static Singleton singleton;
public static Singleton getInstance() {
   if (singleton == null) {
       singleton = new Singleton();
   }
   return singleton;
}

这样的实现,有问题么?是不是感觉棒棒的?

在单线程的环境中跑的话,这样也是可以保证只返回一个instance的。

问题出在多线程环境下执行。

多个线程执行时,极有可能两个线程同时执行到判断是否为null的情况,又同时创建了实例出来。为了解决多线程的问题,你毫不犹豫的给方法加上了synchronized,兵不血刃的解决了问题。

问题又来了!

当这个方法使用很频繁的时候,synchronized带来的互斥效果,导致每次只能一个线程执行,效率很低。

此时,一个聪明的想法浮现在脑海(当然,可能是查资料,网上浏览了解到的)。使用双重锁检查(Double lock checking)来提高效率,实现起来是这个样子:

public static Single getInstance() {
   if(singleton == null) {
       synchronized(this) {
           if(singleton == null) {
               singleton = new Singletoon();
           }
       }
   }
   return singleton;
}

我们的方法并不是互斥的,只有在instance为空时才会加锁检查。看似无懈可击!

这个时候有一个问题,是看似普通的new XXX这种操作,其本质上和i++

这种操作一样,并不是一个原子操作。例如,我们下面这几行代码:

public class Test {
   private int i = 5;
   private int a = 2;
   public Test(int i, int a) {
       this.i = i;
       this.a = a;
   }
   public void ttt() {
      Test t = new Test(1,1); //普通的实例化一个对象
   }
}

大致包含的步骤有:

  1. 创建对象

  2. 初始化对象的各个域,为其赋值

  3. 将对象指向其引用

但是,对于这些指令的执行,却并不一定是按照这个顺序执行,为了执行效率,这些指令会被优化,指令被重新排序。极有可能对象被创建后即指向了其引用,但各个域并没有初始化,如果此时被使用,那拿到的就是一个构造不完整的对象。(可以参考Java并发编程实战了解对象逃逸)

为了使代码不被优化影响,Java 5在修订了Java内存模型(JMM)之后,可以使用volatile声明,不允许指令重排序

volatile关键字同时保证了内存的有序性可见性,保证程序可以按照预期执行。所以,要实现一个正确无误的DCL单例,需要同时把singleton对象声明为

volatile,这一定很重要

如果不使用DCL,我们还有其它方式实现延迟初始化。例如下面这种内部类的形式,也是比较常用的。

public class Foo {
   private static class FooHolder {
       static final Foo foo = new Foo();
   }
   public static Foo getFoo() {
       return FooHolder.foo;
   }
}

由于内部类只有在使用时才会初始化,所以保证了单例的延迟初始化。

了解了以上这些后,我们来看Tomcat中的单例,是如何使用的。

首先我们来看看Tomcat中对于DCL的使用。

/** Whether the servlet needs reloading on next access */
private volatile boolean reload = true;
 

public Servlet getServlet() throws ServletException {
   // DCL on 'reload' requires that 'reload' be volatile
   // (this also forces a read memory barrier, ensuring the
   // new servlet object is read consistently)
   if (reload) {
       synchronized (this) {
           // Synchronizing on jsw enables simultaneous loading
           // of different pages, but not the same page.
           if (reload) {
               // This is to maintain the original protocol.
               destroy();
               final Servlet servlet;
               servlet.init(config);
               reload = false;
               // Volatile 'reload' forces in order write of 'theServlet' and new servlet object
           } }  }
   return theServlet;
}

上面的代码是关于jsp对应的Servlet获取时对应的代码,其中对于DCL的使用主要用于判断jsp文件对应的class是否需要重新加载。(jsp文件工作原理前面文章介绍过,感兴趣的朋友可以看JSP文件修改实时生效的秘密)

单例的使用,Tomcat中的方式很简单,

public final class ApplicationFilterFactory {

   private static ApplicationFilterFactory factory = null;

   private ApplicationFilterFactory() {
       // Prevent instantiation outside of the getInstanceMethod().
   }
 
/**
* Return the factory instance.
*/
public static ApplicationFilterFactory getInstance() {
   if (factory == null) {
       factory = new ApplicationFilterFactory();
   }
   return factory;
}

我们看到和前面第一次提到的饱汉型一样,没有使用DCL,也没加

synchronized,这是因为在Tomcat中对于此处ApplicationFactory的使用,只有在StandardWrapperValve启动才会触发其初始化,并不会涉及到多线程环境的使用,所以可以放心使用这种方式。

看到这里的朋友,其实单例还有一种实现方式,是Effective Java的作者推荐使用的,使用起来更简单,只需要一个枚举项的enum即可,之后可以包含其对应的各个方法:

public enum Singleton {
   INSTANCE;
   public void test() {
       System.out.println("test");
   }
}

而我们使用的时候,直接这样使用即可:

Singleton.INSTANCE.test();

感谢各位的阅读,以上就是“java单例模式实现的方法是什么”的内容了,经过本文的学习后,相信大家对java单例模式实现的方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!


网站栏目:java单例模式实现的方法是什么
分享路径:http://6mz.cn/article/pdgdoc.html

其他资讯