单例模式的几种实现方式
实现方式大致分为两种
- 懒汉模式
- 饿汉模式
懒汉模式:在第一次使用该对象时,才会初始化实例,以免了资源的浪费,同时,需要考虑的是线程安全问题。
饿汉模式:在类初始化时就需要创建一个实例对象出来,当时并不一定会使用该对象,可能会造成资源的浪费,好处是不用考虑安全问题。
下面看下几种常见的实现方式:
首先看懒汉模式:
1、线程非安全版本,可在单线程下使用
/** * 懒汉式单例,线程不安全,只有在第一次调用时才会初始化一次, * 但是线程不安全,如果在初始化时需要消耗大量的资源,则会造成资源的浪费,同时,在并发场景中,可能造成变量的变化等问题。 * @author woniu * */ public class Singleton1 { private Singleton1(){} private static Singleton1 instance = null; public static Singleton1 getInstance() { if (instance == null) { instance = new Singleton1(); } return instance; } }
关于懒汉模式的几种线程安全版本,详细的说明已在类中说明,不在单独说明。
1、在getInstance方法上加同步
/** * 懒汉式单例,线程安全,与第一种无异,仅是通过synchronized,将getInstance实现为了同步方法, * 但是,这就造成了getInstance方法,仅能同时只有一个线程调用,但是,在除去第一次初始化外,我们大多数情况下,并不需要防止同步问题。 * @author woniu */ public class Singleton2 { private Singleton2(){} private static Singleton2 instance = null; public static synchronized Singleton2 getInstance() { if (instance == null) { instance = new Singleton2(); } return instance; } }
2、双重检查锁定
/** * 此方法被称为双重校验锁,在示例2的基础之上将同步方法,修改为了同步代码块,仅是在需要初始化时,才需要加锁,这样就避免了在大多数情况下不需要同步的问题。 * 关于之所以在同步方法块中再次进行判断的原因:根据并发编程实战中的提到的“先检查后执行”的操作是非原子性的,简而言之就是,避免用一个过期的变量作为当前的判断标准。 * 连接:http://www.cnblogs.com/woniu4/p/8284244.html * @author woniu * */ public class Singleton3 { private Singleton3(){} private volatile static Singleton3 instance = null; public static Singleton3 getInstance() { if (instance == null) { synchronized(Singleton3.class) { if (instance == null) { instance = new Singleton3(); } } } return instance; } }
3、静态内部类
/** * 单例同样可以通过内部类的方式实现,这样就避免了同步带来的性能开销(虽说现在synchronize方法已经做了很大的优化,对性能的影响已经降低了很多,但终究还是有一定影响的。) * 虽说这种方式比较好,但是在我们当前项目中,似乎大家都比较懒,直接用了方法2中的模式,毕竟,当前的工程性项目,并没有对项目性能有极高的要求。 * @author woniu * */ public class Singleton4 { private Singleton4(){}; private static class LasyHolder { private static final Singleton4 INSTANCE = new Singleton4(); } public static Singleton4 getInstance() { return LasyHolder.INSTANCE; } }
饿汉模式:
/** * 饿汉模式,通过在类初始化时,已经实例化,这样本身就是线程安全的。 * @author woniu * */ public class Singleton5 { private Singleton5(){} private static final Singleton5 INSTANCE = new Singleton5(); public static Singleton5 getInstance() { return INSTANCE; } }
最后有一种通过枚举实现的方式,算是一种比较新的方式吧。当前不清楚具体归属类型,暂且单列出来。
/** * 枚举模式,没有使用过,仅是博客或者书见过这种方式,不仅能避免线程同步问题,而且还能防止反序列化重新创建新的对象,日后工作中,有类似需要单例场景中,可以考虑使用一下,写法还是十分简单的。 * @author woniu * */ public enum Singleton6 { INSTANCE; public String testMethod() { return "test enum instance"; } public static void main(String[] args) { String str = INSTANCE.testMethod(); System.out.println(str); } }