【自分の総括】设计模式之单例设计模式

前记
上周偶然听到身边人提起这个名词,表现的跟小白一样,后面上网搜了下,发现老早老早就接触过了,只是当时把它当成了一种编程方式了。
现总结如下,部分内容引用自互联网,如有雷同请联系小林。

一.单例模式实现特点:
单例类在整个应用程序中只能有一个实例(通过私有无参构造器实现);
单例类必须自己创建这个实例并且可供其他对象访问(通过静态公开的访问权限修饰的getInstance()方法实现);
实例演示1:

public class Singleton {

    private static Singleton uniqueInstance = null;

    private Singleton() {

        super();
    }

    public static Singleton getInstance() {

        if (uniqueInstance == null) {

            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

分析:构造方法私有化(private修饰)避免了类在外部被实例化,保证在同一个虚拟机范围内,类的唯一实例只能通过getInstance()方法访问;
关getInstance()方法的特点有三点,public修饰、static修饰、创建本类对象并赋给成员变量。

实例演示2:
首先,创建一个单例类

public class Tools {

    private static Tools toolsInstance = null;
    private String name;

    private Tools() {}

    public String getName() {
        return name;
    }

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

    public static Tools getInstance() {

        if (toolsInstance == null) {
            toolsInstance = new Tools();
        }

        return toolsInstance;
    }
}

然后,创建一个简单的测试类,验证单例设计特点是否正确

public class TestTools {

    public static void main(String[] args) {

        Tools person_1 = Tools.getInstance();
        person_1.setName("NICK");
        System.out.println(person_1.getName());

        Tools person_2 = Tools.getInstance();
        person_2.setName("XIAOLIN");
        System.out.println(person_2.getName());

        if (person_1 == person_2) {

            System.out.println("Nick and XiaoLin are the same person !");
        }
    }
}

分析:person_1和person_2看似是两个对象,实际上指向同一个内存区间,因此修改一方的成员变量将影响另一方,最后结果是person_1和person_2成员变量是等值的。

二.单例模式应用扩展
1.懒汉式模式:懒汉式是典型的用时间换空间,也就是每次获取实例都会进行判断,,看是否需要创建实例,因此浪费了一定的程序运行时间,但因此避免了不必要的内存空间的占用。

2.饿汉式模式:饿汉式是典型的用空间换时间,当类加载的时候会创建类实例,故节省了运行时间,但可能浪费了不必要的内存空间。

3.登记式模式:登记式实际对一组单例模式进行的维护,主要是在数量上的扩展,通过map我们把单例存进去,这样在调用时,先判断该单例是否已经创建,是的话直接返回,不是的话创建一个登记到map中,再返回。对于数量又分为固定数量和不固定数量的。下面采用的是不固定数量的方式,在getInstance方法中加上参数(string name)。然后通过子类继承,重写这个方法将name传进去。

实例演示3:

//类似Spring里面的方法,将类名注册,下次从里面直接获取。  
public class Singleton {

    private static Map<String, Singleton> map = new HashMap<String, Singleton>();

    // 静态代码块
    static {
        Singleton single = new Singleton();
        map.put(single.getClass().getName(), single);
    }

    // 保护的默认构造子
    protected Singleton() {
    }

    // 静态工厂方法,返还此类惟一的实例
    public static Singleton getInstance(String name) {

        if (name == null) {
            name = Singleton.class.getName();
            System.out.println("name == null" + "--->name=" + name);
        }

        if (map.get(name) == null) {
            try {
                map.put(name, (Singleton) Class.forName(name).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return map.get(name);
    }

    public static void main(String[] args) {

        Singleton singleTest = Singleton.getInstance(null);
        //System.out.println(singleTest.about());
    }
}

三.线程安全
从线程安全的角度上讲,不加同步的懒汉式是线程不安全的,比如:有两个线程,一个是线程First,一个是线程Second,他们同时调用getInstance()方法,那就可能导致并发问题,因为在First线程中判断instance==null的时候会通过new 创建单例对象,但是此时,可能在Second线程中,也进行了同样的instance==null判断,因为此时,线程First中的对象还没有创建完成,所以线程Second中也会通过new来创建单例对象,那么问题就来了!同时会创建出两个实例来,也就是说单例的作用在并发的情况下失效了。

而饿汉式是线程安全的,因为虚拟机只会装载一次,在装载类的时候是不会发生并发的。可以直接用于多线程而不会出现问题!那么,我们如何实现懒汉式的线程安全呢?很简单,我们只需要通过synchronized修饰即可解决问题,比如:

Public static synchronized Singleton getInstance(){}但是这样一来,会降低整个访问的速度,而且每次都要判断。那么有没有更好的方式来实现呢?

双重检查加锁:

可以使用”双重检查加锁”的方式来实现,就可以既实现线程安全,又能够使性能不受到很大的影响。那么什么是”双重检查加锁”机制呢?

所谓双重检查加锁机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

示例代码如下:

public class Singleton {

    private volatile static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {

        // 先检查实例是否存在,如果不存在才进入下面的同步块
        if (instance == null) {

            // 同步块,线程安全地创建实例
            synchronized (Singleton.class) {

                // 再次检查实例是否存在,如果不存在才真正地创建实例
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }
}

分析:这种实现方式可以实现既线程安全地创建实例,而又不会对性能造成太大的影响。它只是在第一次创建实例的时候同步,以后就不需要同步了,从而加快了运行速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值