单例模式的实现

最近学习到了Java的单例模式及其实现。写这篇博客记录下,也希望帮助到有需要的人。

单例模式简述

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的,可以帮我们高效的构造出好的软件。在C语言课程中我们老师引用了一句非常好的话“程序=算法+数据结构”。而在实际开发中,这句话更多地是“程序=设计模式+数据结构”。由此可见设计模式在实际开发中的重要程度。

单例模式是设计模式中比较简单的一种模式,在实际应用中范围很广。一般只能被独享的资源一般都设计成单例模式,比如计算机系统中的线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象。一个计算机虽然可以接入很多的打印机,但是Printer Spooler只有一个,这样就可以避免两个打印作业输出到一个打印机中。从这个例子中我们可以看出单例模式的优点,即避免出现不一致状态。

那什么是单例模式呢?总的来说,单例模式有下面三个特点:
1、该类的实例化对象只有一个。
2、该类的实例化对象只能由该类来创建
3、该类必须通过方法

单例模式这三个特点决定了实现单例模式的类的构造器是私有的,不允许其他的类去访问;该类的唯一实例化对象的初始化过程应该在类中来实现;该类应该提供公有有的方法来返回该类的唯一对象以给其他类来使用。

单例模式可以由两种大的思路来实现,一个是懒汉式,哪一个是饿汉式。下面就来介绍这两种方式及其变体。

饿汉式

饿汉式顾名思义,就是一种很饥饿的创建方式。怎么体现饥饿呢?即类一被加载,该类的唯一对象就已经创建成功,不管我们去调不调用,该单例类的对象都已经存在内存中了。怎么才能做到这一点呢?Java中提供了static关键字和代码块来实现。下面是实现代码。

public class singleton {

        private singleton(){}

        //静态变量实现
        private  static  singleton instance=new singleton();
        
        //代码块内实现
        /*
        static {
            instance=new singleton();
        }
        */
        public static singleton getInstance() {
            return instance;
        }
}

``
这样做会带来一定的坏处,即万一该对象没有被使用,但是还是会占用一定的内存,浪费内存。但是他也有个好处,即是线程安全的,不用加锁,执行效率高。

为了实现避免内存的浪费,我们引入了懒汉式来完善。

懒汉式

懒汉式不再采用饿汉式那种随类加载的方式来初始化对象,而是把对象的初始化放在了返回类对象的方法中。这样,当我调用该单例类的对象时,该对象才会实例化。但是这样,每调用一次该类的返回对象方法就会实例化一个对象。为了避免这种情况,我们加上了一个判断,如果该对象为空则实例化,反之则直接返回该对象。下面即是代码

public class singleton_notsefe {
    private static singleton_notsefe instance=null;

    private singleton_notsefe(){}

    public static singleton_notsefe getInstance() {
        if(instance==null)
            instance = new singleton_notsefe();
        return instance;
    }
}

在单线程程序中,上面的写法没有问题。但是到了多线程中,就会出现一些问题了。在单核CPU上的多线程程序并不是一个线程运行完在运行下一个线程的,而是多个进程交替运行。
上述程序中当一个线程A运行完条件判断但是还未实例化该单例类的对象被挂起,此时线程B接着运行实例化一个对象,接着A被唤醒接着执行,这时线程A就会再实例化一个对象。这样就破坏了单例性了。

懒汉式(同步机制,线程安全)

Java为了避免多线程程序运行随机的问题,引入了同步机制。同步机制借助同步监视器来保证线程的依次进行。同步监视器就像是一个设计好的房间,里面放有同步代码和资源。当一个线程进入这个“房间”时,同步监视器就自动的把“房间的门”锁上,不让其他的线程进来,知道当前线程运行完毕才重新打开门让别的线程进来运行。这样做实际上多线程就变成了一个个的单线程来运行。

同步机制有三种实现方式,分别是是同步代码块、同步方法、lock锁。下面就依次介绍这三种方法。

同步代码块(多线程常用)

public class singleton {
    private static singleton instance=null;

    private singleton(){}

    public static singleton getInstance() {
        if(instance==null)  //只有第一次实例化才执行下面代码,以后不执行,提高效率
            synchronized (singleton.class) {  //以自身类来做同步监视器
               if(instance==null)
                instance = new singleton();
            }
        return instance;
    }
}

同步方法

public class singleton_way {
    private static singleton_way instance=null;

    private singleton_way(){}

    public static synchronized singleton_way getInstance() {
        if(instance==null)
            instance = new singleton_way();
        return instance;
    }
}

这样就在多线程的情况下完成了高效的单例模式。

内部类式(饿汉式和懒汉式的结合)

内部类式的机制和饿汉式相似,都是利用了类的加载机制。但是内部类的加载不是随外部类加载而加载,是等到我们调用内部类的参数时才加载。这样就实现了懒汉式的用时才初始化的效果。下面是代码:

//外类类加载时,内部类并未被加载。等到调用GetInstacce时才被加载,外部类的对象才被创建
public class singleton {
    private  singleton(){}

    private static class singleton_get{
        private static singleton instance=new singleton();
    }

    public static singleton GetInstance(){
        return singleton_get.instance;
    }
}

枚举类式(本质还是饿汉式,常用)

枚举类是Java 5.0引入的类型,关键字是enum。其原理和饿汉式是一样的。JVM会生成一个singleton类,并在其内部代码块中完成对枚举类中对象的初始化。由于枚举类以及举出了该类所有的对象,不能再添加实例化对象。所有也可以实现单例模式

public enum singleton {
    instance;
    singleton(){}  //私有的构造器

    public void method(){

    }
}

调用时直接使用singleton.instance就可以使用了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值