设计模式总纲——单例设计模式

  前两天写了设计模式总纲,今天就来讲讲我们在工程代码中最最最常用的设计模式了——单例设计模式,这个模式在工程代码上的出现率几乎为99.99999%,但是虽然很常用,但是用的好的人却不多,今天我们就来深入的说一说单例设计模式。

  在学习一项新的知识之前,我们都要向自己提出三个问题,为什么要用这个知识,这个知识用在哪里,这个知识怎么用?既 why,where,how,3W模式,我们先来谈谈为什么需要单例设计模式,先来想想,如果一个工具类,比如一个读取配置文件的工具类,这类工具只是特定的功能类,既读取指定的文件内容,这种类我们在使用的时候只需要建造一个就行了,然后在整个系统之中都用这个类来进行指定文件的读取即可,但是如果在设计之初没有考虑好,并没有把其设计成单例,导致整个系统中分布多个类似的功能类,一方面,导致了系统资源的浪费,如果该配置文件内容较小,对内存来说还好,但是如果是几百M或者几个G的配置文件的内容的话,就会造成系统资源的严重浪费,导致内存泄露,一方面也会让代码显得异常凌乱。

  为了解决这种问题,既对于只是解决单一功能的功能类,我们最好的做法就是将其设计成单例,接下来我们来看看我们要怎么来实现一个单例。

  正所谓万丈高楼平地起,再复杂的功能也是由一行行简单的代码组成的,那我们来看一下,要实现一个单例类的话,首先,肯定是不能让用户自行生产的,那就是说明不能让用户new,所以,就必须把构造函数设置成为私有的。

1 public class Singleton {
2     private Singleton(){}
3 }

好了,这就是单例了,哦,不,这应该是无例,因为把构造函数都弄成私有的了,什么都没有,用户拿到了这个类只能一脸懵逼,既然要变成单例,那肯定要给用户一个实例是吧,既然用户创建不了,那我们就给他一个,如下

 1 public class WorstLazySingleton {
 2     //1、私有化构造函数
 3     private WorstLazySingleton(){}
 4
 5      //2、静态化一个实例,静态的实例保证了在每一个类中都是唯一的
 6     private static WorstLazySingleton instance = null;
 7
 8
 9     //3、返回该对象的实例,也必须是静态的方法,不然无法调用静态的实例
10     public static WorstLazySingleton getInstance(){
11         if(instance == null){
12             instance = new WorstLazySingleton();
13         }
14         return instance;
15     }
16 }

好了,一个新鲜的单例就出炉了,but,是不是有什么问题呢,为什么这个单例被加上了个Worst的标签,这个年代什么最惨,被人随意贴标签最惨,隔着屏幕都能感受到这个单例哀怨的眼神,但是,我们来看一看,这个单例,咋一看在单线程的环境下没问题,但是只要一到了多线程的环境下,妥妥的要出问题啊,随意写了个测试用例,跑了个10条线程来getInstance,竟然出现了4个不一样的hashCode,这个哪里是单例,明显是多的不能再多的”多例“了,好吧,这个worst的标签就先贴上去吧。那有同学就说了,我加同步方法啊,好,我们来为这个类加上同步方法,

  大致如下代码,

 1 public class BadLazySingleton {
 2     private static BadLazySingleton instance = null;
 3
 4     private BadLazySingleton(){}
 5     //加上了synchronized的同步方法
 6     public static synchronized BadLazySingleton getInstance(){
 7         if(instance == null){
 8             instance = new BadLazySingleton();
 9         }
10         return instance;
11     }
12 }

这个方法现在被加上了synchronized了,运行一下多线程的测试环境,咋一看,好像没问题了,但是,我们再想一下下面的场景,如果在方法里面这个对象特别大的话,导致虚拟机调用的时间较长,或者在这个方法里面做了其他的 doSomething()方法的话,那其他线程只能乖乖的等待他的结束了,比如这个 方法执行时间用了10S,那10条线程过来,想想就有点小激动呢,一旦运行在服务器端上,那客户的等待时间,流失率是妥妥的,又有同学要提意见了,我们可以来缩小范围啊,我们不要再在方法上加同步了,好,那我们来看一看下个version的单例,

 1 public class NormalSingleton {
 2     //1、私有化构造方法
 3     private NormalSingleton(){}
 4
 5     //2、静态化一个实例,静态的实例保证了在每一个类中都是唯一的
 6     private static NormalSingleton instance = null;
 7
 8     public static NormalSingleton getInstance(){
 9         if(instance == null){
10             //在局部进行同步,减少线程的等待时间
11             synchronized (NormalSingleton.class) {
12                 //进行双重判断,防止线程到了这一步刚好停住了,导致没有继续往下走而另外一条线程刚好进来
13                 if(instance == null){
14                     instance = new NormalSingleton();
15                 }
16             }
17         }
18         return instance;
19     }
20 }

看来这个版本是比较Normal的Singleton了,不仅进行了同步,而且只需要进行一次同步,即只需要在第一次进行同步即可,还涉及到了双重判断,防止多线程上环境上的串线,这就是所谓的 Double-Check,but,有人就想到,为什么要我们要自己写同步,有的人表示已经累觉不爱了,不喜欢自己写同步了,要榨干JVM的最后一点资源,同步的重任就交给你了,(很用力的拍了拍虚拟机的肩膀),那我们来说一下,什么时候虚拟机会自己给自己加同步。

  1、在静态字段上或static{}块中的初始化数据时

  2、访问final字段时

  3、在创建线程之前创建对象时

  4、线程可以可以看见它将要处理的对象时

那有了这四个条件,我们就可以想象,要让JVM自动来实现同步的话,就可以采用静态初始化器的方式,但是有人就会说了。静态初始化器虽然是同步的,但是类一加载的时候他就会去初始化这个对象了,哪怕我们不需要他也会去加载这些对象,那接下来来个脑经急转弯了,那如果我们可以让这个类在加载的时候不要去初始化这个对象不就可以喽?有人会说,有这等好事???

还真有,这种类就叫做静态内部类,也叫作类级内部类,我们来看看代码:这种方法算是目前最好的方法之一了:(为什么叫之一....因为还有之二....)

 1 public class BestLazySingleton {
 2     //私有化构造方法
 3     private BestLazySingleton(){}
 4
 5     //创建静态的内部类,让JVM自身来保证线程的安全性,而且该类只有在被调用到的时候才会去加载
 6     private static class SingletonHolder {
 7         private static BestLazySingleton instance = new BestLazySingleton();
 8     }
 9
10     public static BestLazySingleton getInstance(){
11         return SingletonHolder.instance;
12     }
13 }

这个类算是目前最好的懒加载的单例范本了,使用类级内部类,通过jvm来进行同步,当我们调用的时候才去初始化,进而实现了延迟加载的特性,而且也没有增加同步方法块,只增加了一个内部域的访问,成本相较前面的几种方法都非常低。

最后我们来讲讲目前最好的单例的方法之二,这个方法是在《Effective Java》书中提到的,通过Enum来实现单例,首先我们需要了解一个大前提,Java中的Enum实质上也是一个功能齐全的类,也可以有自己的属性和方法,而且枚举算是单例的泛型化,本质上是单元素的枚举,而且也可以通过Enum来实现可变的多例类型的“单例”,具体代码如下

 1 public enum EnumSingleton {
 2     //定义一个枚举的元素,就代表了Singleton的一个实例
 3     instance;
 4
 5     private String singletonData;
 6
 7     public String getEnumSingleton(){
 8         return singletonData;
 9     }
10
11     public void setEnumSingleton(String singletonData){
12         this.singletonData = singletonData;
13     }
14 }

也可以类似的写上 instance2,instance3.......对于Enum来说,都是单例,这种实现形式基于JDK1.5以及JDK1.5以上

最后假设你不想使用懒加载的单例模型,你实在表示很想偷懒,那就使用饿汉式的单例吧,这种方法简单粗暴,并且是线程安全的,就是类一旦被加载的时候就会去实例化该对象,哪怕不使用该类的时候,具体代码如下:

 1 public class EagerSingleton {
 2     //直接实例化类实例,其他别无二致
 3     private static EagerSingleton instance = new EagerSingleton();
 4
 5     private EagerSingleton(){}
 6
 7     public static EagerSingleton getInstance() {
 8         return instance;
 9     }
10 }

这种方法简单粗暴,老少咸宜,但是性能如何就见仁见智了,

好了,差不多晚上的JAVA单例设计模式就讲到这里了,最后贴上思维导图一张,就在总纲的基础上在Singletong的设计模式上添加的,下回我们再见,下回我们具体会讲到下一个CreationPattern中的Factory Method,敬请期待。

  如需转载请告知,转载请注明出处。

时间: 2024-10-10 00:34:29

设计模式总纲——单例设计模式的相关文章

java设计模式之单例设计模式

设计模式:解决某一类问题最行之有效的方法. java中23种设计模式. 单例设计模式:解决一类在内存中只存在一个对象. Runtime()方法就是单例设计模式进行设计的. 解决的问题:保证一个类在内存中的对象唯一性. 比如:多程序读取一个配置文件时,建议配置文件封装成对象.会方便操作其中数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在内存中是唯一的. 1.为了避免其他程序过多建立该类对象,先禁止其他程序建立该类对象. 2.还为了让其他程序可以访问该类对象,只好在本类中自定

设计模式之-----------单例设计模式

饿汉式: class Single { //   提前做好! private static final Single s = new Single(); //  私有化 构造函数  无法使用new 创建对象! private Single(){} //  对外提供接口 public static Single getInstance() { return s; } } 懒汉式: 懒汉 顾名思义  就是懒呗 什么时候用到 什么时候创建! class Single1 { private static

java软件设计模式只单例设计模式

概述 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的:设计模式使代码编制真正工程化:设计模式是软件工程的基石脉络,如同大厦的结构一样. 设计模式分为三种类型,共23种.创建型模式:单例模式.抽象工厂模式.建造者模式.工厂模式.原型模式.结构型模式:适配器模式.桥接模式.装饰模式.组合模式.外观模式.享元模式.代理模式.

设计模式之单例设计模式

一.何为单例设计模式 单例模式,顾名思义就是单个实例,程序中某个类只有一个实例存在.通常实在需要共享某个资源避免资源损耗的情况下使用到的. 二.单例设计模式的代码实现 一说到单例模式的概念,我们首先会想到下面的这种的写法 public class SingleInstance { private static SingleInstance singleInstance; /** * 单例模式 * @return */ public static SingleInstance getSingleI

Java设计模式之单例设计模式(Singleton)

单例设计模式 单例模式在日常开发中用的也比较多,顾名思义就是一个类的对象在整个系统中只能有一个 优点: 1.单例模式会阻止其他对象实例化其自己的单例对象副本,从而确保所有对象都访问唯一实例 2.由于在整个系统中指存在一个实例对象,避免了频繁的创建和销毁对象,因此可以节约系统资源 3.避免了对共享资源的多重占用 4.自行创建这个单例对象,避免使用时再去创建 缺点: 1.单例模式没有抽象层,所以扩展性比较差 2.不适用于变化的对象,如果同一类型的对象需要在不同的场景下使用,单例就会引起数据的错误 3

java设计模式_single(单例设计模式)

设计模式:解决某一类问题最行之有效的方法,java中有23种设计模式 一.单例设计模式概述: 1.解决一个类在内存中只有一个对象(保证一个类仅有一个实例,并提供一个访问他的全局访问点)  2.要保证对象的唯一: 1.为了避免其他程序过多的建立该类对象,先禁制其他程序建立该类对象 2.为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象 3.为了 方便其他程序对自定义对象的访问,可以对外提供一些访问方式 3.代码实现步骤: 1.将构造函数私有化 2.在类中创建一个本类对象 3.给外部提供

【设计模式】单例设计模式的N中Java实现方法

特点 单例模式的特点: 1.只能有一个实例: 2.必须自己创建自己的一个实例: 3.必须给所有其他对象提供这一实例. 饿汉式单例模式 也称为预先加载法,实现方式如下: [java] view plaincopy class Single { private Single()( Syustem.out.println("ok"); ) private static Single instance = new Single(); public static Single getInstan

菜鸟之路-浅谈设计模式之单例设计模式

单例设计模式 定义:确保一个类仅仅有一个实例.并且自行实例化并向整个系统提供这个实例. 单例模式是一种经常使用的软件设计模式.在它的核心结构中仅仅包括一个被称为单例的特殊类. 通过单例模式能够保证系统中一个类仅仅有一个实例并且该实例易于外界訪问,从而方便对实例个数的控制并节约系统资源.假设希望在系统中某个类的对象仅仅能存在一个,单例模式是最好的解决方式. 关于单例设计模式的动机 对于系统中的某些类来说,仅仅有一个实例非常重要,比如.一个系统中能够存在多个打印任务.可是仅仅能有一个正在工作的任务:

java设计模式之单例设计模式和多例设计模式

单例设计模式:构造方法私有化,在类的内部定义static属性和方法,利用static方法来取得本类的实例化对象:无论外部产生多少个实例化对象,本质上只有一个实例化对象 饿汉式单例设计 class Singleton{ private static final Singleton INSTANCE = new Singleton();//产生一个实例化对象 private Singleton(){}//封装构造方法 public static Singleton getInstance(){ re