网创优客建站品牌官网
为成都网站建设公司企业提供高品质网站建设
热线:028-86922220
成都专业网站建设公司

定制建站费用3500元

符合中小企业对网站设计、功能常规化式的企业展示型网站建设

成都品牌网站建设

品牌网站建设费用6000元

本套餐主要针对企业品牌型网站、中高端设计、前端互动体验...

成都商城网站建设

商城网站建设费用8000元

商城网站建设因基本功能的需求不同费用上面也有很大的差别...

成都微信网站建设

手机微信网站建站3000元

手机微信网站开发、微信官网、微信商城网站...

建站知识

当前位置:首页 > 建站知识

java单例模式怎么定义

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

创新互联公司长期为1000多家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为兴安盟企业提供专业的做网站、成都做网站,兴安盟网站改版等技术服务。拥有10余年丰富建站经验和众多成功案例,为您定制开发。

一、单例模式定义:
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个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单例模式怎么定义”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!


本文标题:java单例模式怎么定义
地址分享:http://bjjierui.cn/article/jogcge.html

其他资讯