ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

单例(Singleton)

2021-09-14 20:01:51  阅读:112  来源: 互联网

标签:Singleton private INSTANCE 实例 static 单例 public


保证一个类仅有一个实例,并提供一个访问它的全局访问点。

因为这个类只有一个实例,所以我们不能够允许调用方通过new的方式创建实例了。所以,单例的构造方法必须是private,这样就防止了调用方自己创建实例,但是在类的内部,可以通过一个静态字段来引用唯一创建的实例的:

 

public class Singleton {
    // 静态字段引用唯一实例:
    private static final Singleton INSTANCE = new Singleton();

    // private构造方法保证外部无法实例化:
    private Singleton() {
    }
}

 

那么问题来了,外界调用方如何获取这个唯一实例?

答案是提供一个静态方法,直接返回实例:

public class Singleton {
    // 静态字段引用唯一实例:
    private static final Singleton INSTANCE = new Singleton();

    // 通过静态方法返回实例:
    public static Singleton getInstance() {
        return INSTANCE;
    }

    // private构造方法保证外部无法实例化:
    private Singleton() {
    }
}

或者直接把static变量暴露给外部:

public class Singleton {
    // 静态字段引用唯一实例:
    public static final Singleton INSTANCE = new Singleton();

    // private构造方法保证外部无法实例化:
    private Singleton() {
    }
}

总之就是,不给调用方创建实例,允许调用方去获取创建好的实例,所以单例模式的实现方式很简单:

1.只有private构造方法,确保外部无法实例化;

2.通过private static变量持有唯一实例,保证全局唯一性。

3.通过private static方法返回此唯一实例,使外部调用方能够获取到实例。

Java标准库有一些类就是单例,例如Runtime这个类:

Runtime runtime = Runtime.getRuntime();

有些童鞋可能听说过延迟加载,即在调用方第一次调用getInstance()时才初始化全局唯一实例,类似这样:

public class Singleton {
    private static Singleton INSTANCE = null;

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

    private Singleton() {
    }
}

遗憾的是,这种写法在多线程中是错误的,在竞争条件下会创建出多个实例(比如有两个线程 第一个线程A执行到if(INSTANCE==null)了但是接下来它失去了运行资源,cpu的资源给了另一个线程B,然后在这个时候,B线程也是符合if的条件的,所以在这种情况下,就创建了两次对象,无法达到我们要的全局仅此一个实例的效果,这里还会设计到原子性的知识,我之后更新一篇java原子性操作的随笔)。必须对整个方法进行加锁:

public synchronized static Singleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new Singleton();
    }
    return INSTANCE;
}

但是,加锁会严重的影响到并发性能,有的人会听说过双重检查,类似这样:

public static Singleton getInstance() {
    if (INSTANCE == null) {
        synchronized (Singleton.class) {
            if (INSTANCE == null) {
                INSTANCE = new Singleton();
            }
        }
    }
    return INSTANCE;
}

然而,由于Java的内存模型,双重检查在这里不成立。要真正实现延迟加载,只能通过Java的ClassLoader机制完成,没有特殊的需求,使用Singleton模式1的时候,最好不要延迟加载,这样会使代码更简单。

还有一种实现Singleton的方式是利用Java的enum,因为Java保证枚举类的每个美剧都是单例,所以我们只需编写一个只有一个枚举的类即可:

public enum World {
    // 唯一枚举:
    INSTANCE;

    private String name = "world";

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

枚举类也可以完全像其他类那样定义自己的字段,方法,这样上面这个World类在调用方看来就可以这么用:

String name = World.INSTANCE.getName();

使用枚举实现Singleton还避免了第一种方式实现Singleton的一个潜在问题:即序列化和反序列化会绕过普通类的private构造方法从而创建处多个实例,而枚举类就没有这个问题。

那么我们什么时候应该使用Singleton呢?实际上,很多程序,尤其是Web程序,大部分服务类都应该呗视作Singleton,如果全部按Singleton的写法些,会非常麻烦,所以,通常是通过约定让框架(例如Spring)来实例化这些类,保证只有一个实例,调用方自觉通过框架获取实例而不是new操作符:

@Component // 表示一个单例组件
public class MyService {
    ...
}

因此,除非很有表要,Singleton模式一般就是约定,不要去刻意实现。

 

标签:Singleton,private,INSTANCE,实例,static,单例,public
来源: https://www.cnblogs.com/wangjiazhi/p/15269341.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有