Java单例模式的七种写法 布满荆棘的人生 2022-05-29 12:16 174阅读 0赞 # Java单例模式的七种写法 # 1. 第一种(懒汉,线程不安全): 2. 3. public class Singleton \{ 4. private static Singleton instance; 5. private Singleton ()\{\} 6. public static Singleton getInstance() \{ 7. if (instance == null) \{ 8. instance = new Singleton(); 9. \} 10. return instance; 11. \} 12. \} 13. 这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。 14. 15. 第二种(懒汉,线程安全): 16. 17. public class Singleton \{ 18. private static Singleton instance; 19. private Singleton ()\{\} 20. public static synchronized Singleton getInstance() \{ 21. if (instance == null) \{ 22. instance = new Singleton(); 23. \} 24. return instance; 25. \} 26. \} 27. 28. 这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。 29. 30. 第三种(饿汉): 31. 32. public class Singleton \{ 33. private static Singleton instance = new Singleton(); 34. private Singleton ()\{\} 35. public static Singleton getInstance() \{ 36. return instance; 37. \} 38. \} 39. 这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。 40. 41. 第四种(饿汉,变种): 42. 43. public class Singleton \{ 44. private Singleton instance = null; 45. static \{ 46. instance = new Singleton(); 47. \} 48. private Singleton ()\{\} 49. public static Singleton getInstance() \{ 50. return this.instance; 51. \} 52. \} 53. 表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。 54. 55. 第五种(静态内部类): 56. 57. public class Singleton \{ 58. private static class SingletonHolder \{ 59. private static final Singleton INSTANCE = new Singleton(); 60. \} 61. private Singleton ()\{\} 62. public static final Singleton getInstance() \{ 63. return SingletonHolder.INSTANCE; 64. \} 65. \} 66. 67. 这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。 68. 69. 70. 第六种(枚举): 71. 72. public enum Singleton \{ 73. INSTANCE; 74. public void whateverMethod() \{ 75. \} 76. \} 77. 78. 这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。 79. 80. 第七种(双重校验锁): 81. 82. public class Singleton \{ 83. private volatile static Singleton singleton; 84. private Singleton ()\{\} 85. public static Singleton getSingleton() \{ 86. if (singleton == null) \{ 87. synchronized (Singleton.class) \{ 88. if (singleton == null) \{ 89. singleton = new Singleton(); 90. \} 91. \} 92. \} 93. return singleton; 94. \} 95. \} \[html\] [view plain][] [copy][view plain] 1. 总结有两个问题需要注意: 2. 3. 1、如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类 装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。 4. 5. 2、如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。 6. 7. 8. 对第一个问题修复的办法是: 9. 10. private static Class getClass(String classname) 11. throws ClassNotFoundException \{ 12. ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 13. 14. if(classLoader == null) 15. classLoader = Singleton.class.getClassLoader(); 16. return (classLoader.loadClass(classname)); 17. \} 18. \} 19. 20. 对第二个问题修复的办法是: 21. 22. public class Singleton implements java.io.Serializable \{ 23. public static Singleton INSTANCE = new Singleton(); 24. 25. protected Singleton() \{ 26. 27. \} 28. private Object readResolve() \{ 29. return INSTANCE; 30. \} 31. \} [view plain]: https://blog.csdn.net/FiangAsDre/article/details/79544008#
还没有评论,来说两句吧...