java单例模式怎么定义

发布时间:2021-12-30 09:26:18 作者:iii
来源:亿速云 阅读:122

本篇内容介绍了“java单例模式怎么定义”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、单例模式定义:
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

1、经典饿汉式:

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return INSTANCE;
    }
}
特点:程序启动时加载,先加载类,再初始化静态属性,由于后面无法再对对象进行修改,从而实现线程安全,效率相对高一些。占用内存相对多一些。

缺点:如果这个类特别庞大,初始化时将会特别缓慢,还有就是如果我们用不到这个类,它仍然会创建出来,浪费了资源。

2、经典懒汉式:

public class Singleton {
    private static Singleton singleton;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
特点:延时加载,节约了内存。效率相对低一些。利用同步块实现线程安全。

缺点:synchronized关键字是一个重锁(对象锁),它会每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了。

3、懒汉式变种—双重检查结构(不加volatile关键字修饰):

package cn.hzy.creationPattern.singleton;
 
public class Singleton3 {
    private static Singleton3 instance = null;
    private Singleton3(){    
    }
    public static Singleton3 getInstance(){
        if (instance == null) {
            synchronized (instance) {
            if (instance == null) {
                instance = new Singleton3();
        }
        }
        }
    return instance;
    }
}
特点:属于懒汉式的变种,上面懒汉式的特点都有,但是这里优化了性能问题,没有给getInstance()方法加锁,而是只给instance = new Singleton3();加锁,也就是说只在初始化的时候会加锁,后面的访问因为instance!=null,就不会加锁。

缺点:乍一看这种模式既没有线程安全问题,又保证了单例,貌似完美了,但是JVM在创建对象的时候有可能为了优化性能而进行指令重排,

看似简单的一句     instance = new Singleton3();   JVM在创建对象的时候会有三个步骤:

1、给Singleton3分配一个内存空间

2、初始化Singleton3(也就是创建Singleton3对象)

3、将instance指向刚分配的内存空间地址

但是有可能JVM为了编译的优化提高效率就有可能变成下面一种顺序:

1、给Singleton3分配一个内存空间

2、将instance指向刚分配的内存空间地址

3、初始化Singleton3(也就是创建Singleton3对象)

其实这种情况在单线程情况下是毫无影响的,结果都一样,但是如果在多线程情况下,就有可能导致错误。

比如:A、B两个线程访问getInstance()方法,A先进入第一个if判断,然后进入synchronized块,开始初始化Singleton3,由于发生了指令重排,将instance指向刚分配的内存空间地址(此时未创建Singleton3对象),在这个时候,B访问getInstance()方法,B进入第一个if判断,因为instance已经指向了一个存在的内存空间地址,即instance!=null,此时直接返回instance(未初始化),然后再调用的时候如果A还没有初始化完毕那么就会报空指针错误。(概率很低)

解决方案:加上volatile关键字修饰,

volatile:

特性一:内存可见性,即线程A对volatile变量的修改,其他线程获取的volatile变量都是最新的。

特性二:可以禁止指令重排序。

修改如下:将   private static Singleton3 instance = null;   改为   private static volatile Singleton3 instance = null;

4、静态内部类:

package cn.hzy.creationPattern.singleton;
 
public class Singleton4 {
    private Singleton4() {}
    
    public static Singleton4 getInstance() {
        return SingletonFactory.instance;
    }
        
    private static class SingletonFactory {
    private static Singleton4 instance = new Singleton4();
    }
}
特点:按特征也是属于懒汉模式,因为只会在我们需要用的时候才会创建实例对象,这里通过构造函数私有化,使用内部类来维护单例的实现,因为JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次, 并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心Singleton3出现的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。

缺点:貌似这个就完美了,但是静态内部类也有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,故外部无法传递参数进去的。

5、枚举:

public enum Singleton {
    INSTANCE;
    public void method() {
    }
}
直接调用SingleTon.INSTANCE就是单例。

特点:创建枚举默认就是线程安全的

优点:简直不要太多,1、写法简单,对比上面的实例就能发现。2、可以防止反射攻击。

针对上面的反射攻击我这里简单说一下:在上面的1、2、3、4种单例模式里面,如果不对构造函数做一些安全处理,我们可以很轻松通过反射拿到构造器并且创建不只一个实例对象,就不再是单例了。但是对于枚举,即时你通过反射拿到构造器,在创建对象实例的时候也会报错,因为枚举是可以防止反射攻击的。

怎么对构造函数做一些安全处理?

可以立一个flag,在创建一个对象实例后,改变flag的值,通过判断抛出异常。

比如:

private static boolean flag = false;
private Singleton (){
    synchronized (Singleton .class) {
        if(false == flag){
            flag = !flag;
        } else {
            throw new RuntimeException("单例模式正在被反射攻击!!!");
        }  
    }
}
通过在构造函数里面增加一个判断来保证不被反射攻击。

“java单例模式怎么定义”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

推荐阅读:
  1. Java 单例模式
  2. Java单例模式

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

java

上一篇:基于RESTFul Api操作方法是什么

下一篇:IdentityServer4中OpenID Connect如何添加用户认证

相关阅读

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

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