方式一:饿汉模式public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }
在ClassLoader加载类时,实例化出一个对象,其后使用时返回该对象。
- 优点:线程安全,代码简洁
- 缺点:实例长期被静态成员持有,从类加载开始就一直常驻内存
方式二:懒汉模式public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
在实例被使用时初始化,采用懒加载的方式,所以俗称懒汉模式。
- 优点:在使用时加载,提高资源利用率和程序的运行效率
- 缺点:多线程场景下线程不安全
方式三:线程安全模式
饿汉模式资源利用率低,懒汉模式线程不安全,于是就有了线程安全的懒汉模式
这种模式有几种写法
代码1public class Singleton { private static Singleton instance; private Singleton() { } public synchronized static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }代码2public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; } }代码3public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { instance = new Singleton(); } } return instance; } }
代码1和代码2,本质上是一样的,现货区类的class对象同步锁,然后判断对象是否为空,为空则实例化对象,随后返回对象,这两种模式是可以实现线程安全的,缺点是,每次调用getInstance()获取对象,都要活动类的class对象的同步对象锁;至于代码3,是不能实习线程安全的,因为在判空阶段没有使用同步代码块,对象还是有可能会重复创建。
综合代码1、2、3,得出以下实现方式:
代码4:public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) {//第一次校验,如果不对象为空,直接返回,不必获取同步对象锁 synchronized (Singleton.class) { if (instance == null) {//第二次校验,获取同步对象锁之后再去检验 instance = new Singleton(); } } } return instance; } }
在获取类的class同步对象前后,各做一次判断,有效防止对象多次创建。这种方式仍然是不稳定的,jdk1.5之后引入了volatile关键字。
代码5:双重校验锁式public class Singleton { private static volatile Singleton instance;//这里使用了volatile关键字 private Singleton() { } public static Singleton getInstance() { if (instance == null) {//第一次校验,如果不对象为空,直接返回,不必获取同步对象锁 synchronized (Singleton.class) { if (instance == null) {//第二次校验,获取同步对象锁之后再去检验 instance = new Singleton(); } } } return instance; } }
volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。也就是说,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。
这种方式一般称为双重校验锁式,也是我最喜欢使用的一种方式。
- 优点:线程安全,实现懒加载,资源利用利用率和运行效率较高
- 缺点:代码量稍微大了些,jdk1.5之前不稳定
方式四:静态内部类式public class Singleton { private Singleton() { } public static Singleton getInstance() { return InnerStaticClass.singleton; } private static class InnerStaticClass { private static Singleton singleton = new Singleton(); } }
这里先补充下类加载的相关知识,内部类跟外部类不是同时加载的,是在内部类第一次被使用时加载。
这种方式和饿汉模式一样是利用了Java的类加载器,保证了实例唯一,同事有保证了又保留了懒汉模式的懒加载特性。
- 优点:线程安全,实现懒加载,资源利用利用率和运行效率较高
- 缺点:多写了一个类
特殊:枚举实现单例public enum Singleton { Instance;//只有一个成员 public void set(){ } }
当枚举只有只有一个成员时,这个成员就是它的唯一实例,这样使用
Singleton.Instance.set();
登录 | 立即注册