设计模式的难点---单例模式

单例模式在设计模式的模式使用上很简单但是博主的意思单例模式的使用需要很深的java基础;这是小编认为单例模式的难点;

  • 定义
  • 要点
  • java中的实现方式
  • 1.饿汉式单例
    • 优点
    • 问题
  • 2.懒汉式单例
    • 缺点
    • 双重检查锁定
  • 两种加载模式的 比较
  • 使用内部类

定义

单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。

要点

单例模式有三个要点:
一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。自行提供的时机可以是类加载的时机。也可以是在使用这个类的时候
设计模式的难点---单例模式_第1张图片

Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的
getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。

java中的实现方式

1.饿汉式单例

由于在定义静态变量的时候实例化单例类,因此在类加载的时候就已经
创建了单例对象,代码如下所示:

class EagerSingleton {
        private static final EagerSingleton instance = new EagerSingleton();
        private EagerSingleton() { }
    public static EagerSingleton getInstance() {
        return instance;
    }
}

当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。如果使用饿汉式单例来实现负载均衡器LoadBalancer类的设计,则不会出现创建多个单例对象的情况,可确保单例对象的唯一性.

优点

在类的加载时机类对象已经被创建所以避免的多线程问题:

问题

由于在加载时机创建并赋值对象。造成加载花费的时间大。

2.懒汉式单例

懒汉式单例在第一次调用getInstance()方法时实例化,在类加载时并不自
行实例化,这种技术又称为延迟加载(Lazy Load)技术,即需要的时候再加载实例,为了避免多个线程同时调用getInstance()方法,我们可以使用关键字synchronized。

class LazySingleton {
    private static LazySingleton instance = null;
    private LazySingleton() { }
    synchronized public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

缺点

每次调用getInstance()时都需要进行线程锁定判断,在多线程高并发访问环境中,将会导致系统性能大大降低。
改进方式:

双重检查锁定

class LazySingleton {
    private volatile static LazySingleton instance = null;
    private LazySingleton() { }
        public static LazySingleton getInstance() {
        //第一重判断
        if (instance == null) {
            //锁定代码块
            synchronized (LazySingleton.class) {
            //第二重判断
                if (instance == null) { 
                    instance = new LazySingleton(); //创建单例实例
                }
            }
        }
    return instance;
    }
}

如果使用双重检查锁定来实现懒汉式单例类,需要在静态成员变量instance之
前增加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能够正确处理,且该代码只能在JDK 1.5及以上版本中才能正确执行。由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,因此即使使用双重检查锁定来实现单例模式也不是一种完美的实现方式。
这里的volatile 的双重检测机制的在jdk 1.5之间有bug.在1.5后修复了。这是小编在《深入理解java虚拟机》中看到的。这里的volatile 的有两个作用;其中的一个作用在java虚拟机中就是指的指令重排序问题。

两种加载模式的 比较

摘自:java设计模式

.饿汉式单例类与懒汉式单例类比较
饿汉式单例类在类被加载时就将自己实例化,它的优点在于无须考虑多线程访问问题,可以确保实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一开始就得以创建,因此要优于懒汉式单例。但是无论系统在运行时是否需要使用该单例对象,由于在类加载时该对象就需要创建,因此从资源利用效率角度来讲,饿汉式单例不及懒汉式单例,而且在系统加载时由于需要创建饿汉式单例对象,加载时间可能会比较长。
懒汉式单例类在第一次使用时创建,无须一直占用系统资源,实现了延迟加载,但是必须处理好多个线程同时访问的问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费大量时间,这意味着出现多线程同时首次引用此类的机率变得较大,需要通过双重检查锁定等机制进行控制,这将导致系统性能受到一定影响。

缺点是:
一个是:无论用不用到都会创建导致加载时间长。
另一个是:多线程问题导致性能下降。

使用内部类

Initialization Demand Holder (IoDH)的技术
在IoDH中,我们在单例类中增加一个静态(static)内部类,在该内部类中创建单例对象,再将该单例对象通过getInstance()方法返回给外部使用,实现代码如下所示:

//Initialization on Demand Holder
class Singleton {
    private Singleton() {

    }
    private static class HolderClass {
        private final static Singleton instance = new Singleton();
    }
    public static Singleton getInstance() {
        return HolderClass.instance;
    }
    public static void main(String args[]) {
        Singleton s1, s2;
        s1 = Singleton.getInstance();
        s2 = Singleton.getInstance();
        System.out.println(s1==s2);
    }
}

现在你在看看菜鸟教程的是不是一目了然,还是那句话。想学好习,不要看什么菜鸟教程的垃圾东西。

你可能感兴趣的:(设计模式)