深入浅出设计模式——单例模式

1.单例模式介绍
2.用代码演示单例模式
3.总结
1.单例模式介绍
定义:所谓单例模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个实例,并且该类只提供一个获取其对象实例的方法(静态方法)。
描述:
有些时候,我们系统只需要一个全局的对象,这样有利于我们协调系统的整体行为,节约资源。
比如说:
1)在某个服务中,需要一个对象来读取配置文件,那么有一个这样的对象来读取配置文件也就足够了。
2)比如说我们现在要提供一个线程池,那么大部分情况下线程池这种比较重量级的对象,几个功能模块统一有一个也就够了,因为他会有一个常驻线程数,如果线程池多了会浪费资源,所以这种对象也适合单例的。
以上这两种情况只用了单例的对象,节约了资源,简化了管理。
2.用代码演示单例模式
基本的实现思路
可以看出,单例模式要求一个服务只有一个这样的对象(永远都是相同对象),而且只有一个获取该实例的方法。
根据这个要求,主要实现有以下两个步骤:
1)将构造方法私有化,这样其它的代码就无法调用到该类的构造方法,就无法使用new的方法来创建对象,只能通过该类提供的静态方法来得到该类的唯一实例。
2)在该类内提供一个静态方法,当我们调用这个方法的时候,就能返回单例对象的引用。
单例模式一共有8种写法,
1)饿汉式(静态常量)
2)饿汉式(静态代码块)
3)懒汉式(线程不安全)
4)懒汉式(线程安全,同步写法)
5)懒汉式(线程安全,同步代码块)
6)双重检查
7)静态内部类
8)枚举
我们来每一种写法都来介绍一遍:
1)饿汉式(静态常量 可用)

public class Singleton {private static Object target = new Object(); private Singleton() { }public static Object getInstance(){ return target; }}

优点:写法比较简单,就是在类加载的时候就已经完成了实例化。
缺点:在类装载的时候就完成初始化,没有达到lazy Loading的效果。如果从始至终都未用过这个实例,则可能会造成内存浪费。
2)饿汉式(静态代码块 可用)
public class Singleton {private static Object target = new Object(); private Singleton() { }public static Object getInstance(){ return target; } }

这种方式其实和第一种类似,优缺点也是一样的。
3)懒汉式(线程不安全 不可用)
public class Singleton3 {private static Object target =null; private Singleton3() { }public static Object getInstance(){ if(target==null){ target = new Object(); } return target; }}

优点:起到了懒加载的效果,可以或多或少节约一点资源。
缺点:只能在单线程的模式下,才适用,多线程可能会创建多个单例对象,造成资源浪费。(线程A进入了if判断之后,时间片结束,另一个线程B也通过了这个语句,然后又调度线程A,继续又创建了一个对象,就产生了多个实例。)
4)懒汉式(线程安全,同步写法 不推荐使用)
public class Singleton4 {private static Object target =null; private Singleton4() { }public static synchronized Object getInstance(){ if(target==null){ target = new Object(); } return target; } }

优点:有懒加载的效果,又线程安全。
缺点:效率太低了,不仅仅创建的时候,每个线程想获得实例的时候,都要进行同步,其实只要在要实例的时候进行同步就行了,如果对象已经存在的话直接返回就好了。
5)懒汉式(线程安全,同步代码块 不可用)
public class Singleton5 {private static Object target = null; private Singleton5() { }public static Object getInstance() { if (target == null) { synchronized (Singleton5.class) { target = new Object(); } } return target; }}

优点:优化了第四种方案,可以让获取对象的速度变快
缺点:还是会有线程安全问题,(线程A进入了if判断之后,时间片结束,另一个线程B也通过了这个语句,然后又调度线程A,继续又创建了一个对象,就产生了多个实例。)
6)双重检查 推荐使用
public class Singleton6 {private static Object target = null; private Singleton6() { }public static Object getInstance() { if (target == null) { synchronized (Singleton6.class) { if (target == null) { target = new Object(); } } } return target; }}

优点:进行了两次检查之后,就不怕多线程同时读取的情况了。
7)静态内部类 推荐使用
public class Singleton7 {private static class SingletonInstance { private static final Object target = new Object(); }private Singleton7() { }public static Object getInstance() { return SingletonInstance.target; }}

【深入浅出设计模式——单例模式】优点:类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
8)枚举 推荐使用
public enum SingletonEnum {INSTANCE; public void doSomething() { System.out.println("doSomething"); }}

优点:保证了安全性和唯一性。
缺点:所以在实际项目开发中,很少见人这么写过
3.总结
单例模式应该是设计模式中最简单的一种模式了。它有以下几个要素:
1)私有的构造方法
2)指向自己实例的私有静态引用
3)以自己实例为返回值的静态的公有的方法
至于使用哪种方式获取,一般视情况而定。

    推荐阅读