标签:速度 创建 静态 示例 单例 模式 rgs 缺点 加锁
单例模式(Singleton Pattern):用来创建独一无二的,只能有一个实例的对象的入场券。
在我们进行开发的时候,有些对象我们只需要一个,比如:配置文件,工具类,线程池、缓存、日志对象等。如果创造出多个实例,就会导致许多问题,比如占用资源过多,不一致的结果等。使用单例模式就能保证在程序中需要的实例只有一个。
单例模式的类型:懒汉模式、饿汉模式。
下面是单例模式的类图:
我们将创建一个单例对象类 - Singleton。单例对象(Singleton)类的构造函数是私有的,并且具有自身的静态实例。
Singleton类提供了一个静态方法来获取其静态实例到外部世界。SingletonTest或示例类将使用Singleton类提供的静态方法来获取Singleton对象。
饿汉式是在创建自身的静态实例的时候就直接实例化单例对象,然后在提供的静态方法中直接返回这个实例对象。
1 /** 2 * 单例模式(饿汉模式) 3 * @author admin 4 * 5 */ 6 public class Singleton { 7 //1.将构造方法私有化,不允许外部直接创建对象 8 private Singleton(){ 9 } 10 11 //2.创建类的唯一实例,使用private,static修饰 12 private static Singleton instance = new Singleton(); 13 14 //3.提供一个用于获取实例的方法,使用public static 修饰 15 public static Singleton getInstance(){ 16 return instance; 17 } 18 }
懒汉式在创建自身的静态实例的时候不直接实例化单例对象,只有在外部调用获取单例对象的静态方法的时候才会判断单例对象是否已经存在,如果 不存在则创建一个并返回,如果已经存在则直接返回。
1 /** 2 * 单例模式(懒汉模式) 3 * @author admin 4 */ 5 public class Singleton2 { 6 //1.将构造方法私有化,不允许外边直接创建对象 7 private Singleton2(){ 8 } 9 //2.声明类的唯一实例,使用private,static修饰,但是此处不实例化 10 private static Singleton2 instance; 11 //3.提供一个用于获取实例的方法,使用public static修饰 12 public static Singleton2 getInstance(){ 13 if (instance == null) { 14 instance = new Singleton2(); 15 } 16 return instance; 17 } 18 }
1 /** 2 * 单例模式的测试类 3 * @author admin 4 * 5 */ 6 public class SingletonTest { 7 public static void main(String[] args) { 8 //饿汉模式 9 Singleton s1 = Singleton.getInstance(); 10 Singleton s2 = Singleton.getInstance(); 11 12 if (s1 == s2) { 13 System.out.println("s1和s2是同一个对象"); 14 }else { 15 System.out.println("s1和s2不是同一个对象"); 16 } 17 18 //懒汉模式 19 Singleton2 s3 = Singleton2.getInstance(); 20 Singleton2 s4 = Singleton2.getInstance(); 21 if (s3 == s4) { 22 System.out.println("s3和s4是同一个对象"); 23 }else { 24 System.out.println("s3和s4不是同一个对象"); 25 } 26 } 27 }
测试结果:
从测试结果可以看出来,获取的对象是同一个对象,也就是说,返回的是单例对象。
上面的代码在普通的应用程序中没有任何任何问题,但是在多线程中使用的时候就会发现,返回的单例对象并不是唯一的,而且多个不同的单例对象,这就说明在多线程中,单例模式产生的单例对象并不是"唯一"的。要解决这个问题,有3种办法(其中两种方法都是针对懒汉式的),这3种方法在解决多线程问题的同时也有自己的缺点:
1 /** 2 * 单例模式(懒汉模式) 3 * @author admin 4 */ 5 public class Singleton2 { 6 //1.将构造方法私有化,不允许外边直接创建对象 7 private Singleton2(){ 8 9 } 10 //2.声明类的唯一实例,使用private,static修饰,但是此处不实例化 11 private volatile static Singleton2 instance; 12 //3.提供一个用于获取实例的方法,使用public static修饰 13 public static Singleton2 getInstance(){//检查实例,如果不存在就进入同步区块 14 if (instance == null) { 15 synchronized (Singleton2.class){//注意,只有第一次才彻底执行这里的代码 16 if (instance == null) {//进入区块后,再检查一次,如果还是null,才创建实例 17 instance = new Singleton2(); 18 } 19 } 20 } 21 return instance; 22 } 23 }
volatile关键字确保:当单例变量被初始化成Singleton实例时,多个线程正确地处理单例变量。如果性能问题是关注的重点,那么这个方法可以大大地减少getInstance()的时间消耗。但是该方法也有其缺点,那就是双重检查加锁不适用于1.4及更早版本的Java。在1.4及其更早的版本中,许多JVM对于volatile关键字的实现会导致双重检查加锁的实效。如果不能使用Java5以上的版本,而必须使用旧版本,那么该方法就无法解决多线程的问题。
好了单例模式的叙述到此就结束了,如果有什么讲解的不正确的地方,欢迎大家多多指教!
文章部分内容引用自如下地址:http://www.yiibai.com/design_pattern/singleton_pattern.html
标签:速度 创建 静态 示例 单例 模式 rgs 缺点 加锁
原文地址:http://www.cnblogs.com/gouyadong/p/6947332.html