单例模式顾名思义也就是只有一个实例,并且它自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
下面介绍下单例模式的几种实现方式:
1.懒汉式
懒汉式顾名思义就是这种实例化方式比较“懒”,只有在需要的时候才去创建实例对象。用的时候才去检查有没有实例,如果有则返回,没有则新建。有线程安全和线程不安全两种创建方式。
线程安全的方式:在多线程的条件下,可能导致创建的对象不唯一,存在线程安全的问题。
1 public class Singleton { 2 private static Singleton instance = null; 3 4 private Singleton() { 5 } 6 7 //懒汉式 8 public static Singleton getInstance() { 9 if (instance == null) { 10 instance = new Singleton(); 11 } 12 return instance; 13 } 14 15 }
线程安全的方式:
1 public class Singleton { 2 private static Singleton instance = null; 3 4 private Singleton() { 5 } 6 7 //懒汉式 线程安全 8 public static synchronized Singleton getInstance() { 9 if (instance == null) { 10 instance = new Singleton(); 11 } 12 return instance; 13 } 14 15 }
该方式能够保证线程安全,但是每次调用方法获取对象的时候,都加锁,执行效率低下。
2.饿汉式
从名字上也很好理解,就是“比较勤”,在类加载的时候就初始化了,不管你有没有用到,都先建好了再说。优点是实现简单,没有线程安全的问题,缺点是容易产生垃圾对象浪费内存空间。
1 public class Singleton { 2 3 private Singleton() { 4 } 5 6 private static Singleton instance2 = new Singleton(); 7 8 //饿汉式 9 public static Singleton getInstance2() { 10 return instance2; 11 } 12 }
3.双重锁模式
该模式结合饿汉式和懒汉式的优点,在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还避免类加载的时候就创建对象节省了内存空间。
1 public class Singleton { 2 3 private Singleton() { 4 } 5 6 private static volatile Singleton instance = null; 7 8 public static Singleton getInstance1() { 9 if (instance == null) { 10 synchronized (Singleton.class) { 11 if (instance == null) { 12 instance = new Singleton(); 13 } 14 } 15 } 16 return instance; 17 } 18 }
4.静态内部类的方式
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟饿汉式不同的是:饿汉式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。
1 public class Singleton { 2 3 private Singleton() { 4 } 5 6 public static Singleton getInstance() { 7 return Inner.instance; 8 } 9 10 public static class Inner { 11 private static final Singleton instance = new Singleton(); 12 } 13 }
5.枚举方式
1 public enum Singleton { 2 INSTANCE; 3 public void whateverMethod() { 4 } 5 }
枚举的方式是比较少见的一种实现方式,但是看上面的代码实现,却更简洁清晰。并且她还自动支持序列化机制,绝对防止多次实例化。
上面就是单例模式的五种主要写法。我们来总结下,一般情况下,懒汉式(包含线程安全和线程不安全两种实现方式)都不推荐使用;饿汉式可以使用,可根据具体情况自主选择;在要明确实现 lazy loading 效果时,可以考虑静态内部类或者双重锁检验的实现方式;若涉及到反序列化创建对象时,大家也可以尝试使用枚举方式。
原文:https://www.cnblogs.com/seedss/p/12777142.html