温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2022-01-14 10:45:43 阅读:162 作者:iii 栏目:大数据
Java开发者专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

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

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

  • 饱汉型

  • 饿汉型

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

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单例模式实现的方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

原文链接:https://my.oschina.net/u/4585957/blog/4583167

AI

开发者交流群×