概述

有时候有这样一个需求:一个类对外只提供一个实例,并且只能有一个实例.
这时候就需要引入单例模式这种设计模式

分类

  • 饿汉式
    类加载完成时就完成了对类实例的实例化。
    优点:不涉及多线程问题,不需要考虑线程安全问题。
    缺点:类一加载就实例化,提前占用系统资源

  • 懒汉式
    类加载完成时不进行类实例的实例化,而是在类的实例使用的时候才进行实例化。
    优点:类一加载不对实例进行实例化,而是在使用时进行实例化。
    缺点:涉及多线程问题,需要考虑线程安全问题

饿汉式

饿汉式天生就是线程安全的,直接用于多线程不会出现问题。
实现代码:

public class Single {        private static final Single single = new Single();        private Single(){} // 构造方法私有化
        public static Single getInstance() {            return single;
        }
}

懒汉式

懒汉式本身就是非线程安全的,下面的例1展示了非线程安全的懒汉式,例2,3,4是实现懒汉式线程安全的3中方式。

1. 懒汉式(一定存在线程安全问题)

实现代码:

public class Single {    private static Single single;    private Single(){} // 构造方法私有化
    public static Single getInstance() {        if (single == null) {
            single = new Single();
        }        return single;
    }
}

注意: 在单线程状态下,这个懒汉式的单例可以正常工作,但在多线程环境下,仍然能产生多个实例。这就不符合最初的要求了,可以考虑加入锁的机制。

2. 懒汉式(在方法上进行 synchronized限制)

实现代码:

public class Single {    private static Single single;    private Single(){} // 构造方法私有化
    public static synchronized Single getInstance() {        if (single == null) {
            single = new Single();
        }        return single;
    }
}

该解决方案是在 getInstance() 方法上进行的加锁,同一时间内,只能有一个线程可以进入该方法,在这里就约等于是一个单线程,大大降低了系统的性能。

3. 懒汉式(双检测锁机制)

实现代码:

public class Single {    private static Single single;    private Single(){} // 构造方法私有化
    public static Single getInstance() {        if (single == null) {            synchronized(Single.class) {                if (single == null) {
                    single = new Single();
                }
            }
        }        return single;
    }
}

该解决方案可以避免每一次获取实例都要进行排队获取,一旦single被实例化之后,就永远不会进入synchronized代码块内。这时候对于系统的性能就不会产生影响,只是在第一次实例化的时候会对系统性能产生影响。
当外部程序想要获取Single类的实例的时候,会首先判断一下 single 实例是否为null,然后进行线程控制代码块(synchronized 在同一时刻最多只有一个线程执行该段代码),然后内部又进行了一下 single 实例的判断。然后才进行single实例的实例化操作。
为什么在synchronized代码块中又进行了一次 single是否为null的判断呢?
有这么一种情况就是:同时有2个或者n个线程同时进入 geInstance(), 这时候它们同时进行 single是否为null的判断,所以都进入了 if 代码块内,因为 synchronized只允许单个线程进入,所以有 n-1个线程在synchronized代码块外进行等待,当第一个进入synchronized代码块的线程实例化出 single对象时,synchronized代码块会把后面等待的其它线程依次放进来,这时候single的实例已经被第一个进入的线程实例化了,所以还要进行依次 single的是否为null的判断,这是必须要进行的,不然就会产生多个实例。

4. 懒汉式(静态内部类方式实现)(推荐)

实现代码如下:

public class Single {    private Single(){} // 构造方法私有化
    private static class SingleInstance {        public static final Single single = new Single();
    }    public static Single getInstance() {        return SingleInstance.single;
    }
}

使用静态内部类的特点:加载外部类的时候不对静态内部类进行加载,当使用静态内部类时才会去加载该类,抛弃了锁的使用并且不会提前占用系统资源。是目前比较完美的解决方法。

分类: Java

http://www.cnblogs.com/wuqinglong/p/7125518.html