`
chriszeng87
  • 浏览: 717466 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

线程安全的单例模式

阅读更多

    老大一直说要用这个来做面试题,让面试的人来写一个线程安全的单例模式,感觉自己还没掌握,把 Head first--Design Patterns 找了出来,以下大部分内容抄袭自这本书,略有增删改。

 

1. 全局变量的缺点:

 

   必须在程序一开始就创建好对象,如果程序在这次的执行过程中又一直没用到它,就非常耗费资源。

 

2. 经典的单例模式实现:

 

 

public class Singleton { 
      //用一个静态变量来记录Singleton类的唯一实例
      private static Singleton uniqueInstance;
 
      private Singleton() {}
        
      //注意这个方法也是静态的
      public static Singleton getInstance() { 
      	   if(uniqueInstance == null) {
           	 uniqueInstance = new Singleton();
           }
           return uniqueInstance;
      }
}
 

 

 

    单例常被用来管理共享的资源,例如数据库连接、线程池、缓存、注册表。

    单例模式确保一个类只有一个实例,并提供一个全局访问点。

 

    这个模式的问题:在多线程时,并不能保证这个类只被实例化一次。

 

3. 处理多线程:

 

    public class Singleton {

    //用一个静态变量来记录Singleton类的唯一实例
    private static Singleton uniqueInstance;
 
    private Singleton() {}
        
    //注意这个方法也是静态的
    public static synchronized Singleton getInstance() { 
        if(uniqueInstance == null) {
             uniqueInstance = new Singleton();
         }
         return uniqueInstance;
    }
}
 

   通过增加synchronized关键字到getInstance()方法中,迫使每个线程在进入方法之前,要先等别的线程离开该方法。也就是说,不会有两个线程可以同时进入这个方法。

 

   这种方法存在的问题:只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种浪费。

 

4.改善多线程

 

4.1 如果getInstance()的性能对应用程序不是很关键,就不用优化了

 

4.2 使用急切创建实例,而不用延迟实例化的做法

 

 

public class Singleton { 

    private static Singleton uniqueInstance = new Singleton();
 
    private Singleton() {}
        
    public static Singleton getInstance() { 
         return uniqueInstance;
    }
}
 

   标红的语句在静态初始化器(static initializer)中创建单例,这保证了线程安全

   利用这个做法,JVM在加载这个类时马上创建此唯一的单件实例。JVM保证任何线程访问uniqueInstance静态变量之前,一定先创建些实例。

 

   4.3 用“双重检查加锁”,在getInstance()中减少使用同步

 

    首先检查实例是否已经创建,如果尚未创建,才进行同步。这样一来,只有第一次会同步,这正是我们想要的。

 

 

public class Singleton { 

    private volatile static Singleton uniqueInstance;
 
    private Singleton() {}
        
    public static Singleton getInstance() { 
	if(uniqueInstance == null) { //(1)
        //只有第一次才彻底执行这里的代码
	   synchronized() {
	      //再检查一次
	      if(uniqueInstance == null)
		uniqueInstance = new Singleton();
   	   }
	}
         return uniqueInstance;
    }
}
 

   在最开始如果有1、2、3个线程走到了(1)处,假设1进入了同步块,2、3等待。1实例化后,2进入同步块,发现uniqueInstance已经不为空,跳出同步块。接着3进入,又跳出同步块。

 

    volatile关键字确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地uniqueInstance变量。如果性能是你关心的重点,那么这个做法可以帮你大大地减少getInstance()的时间耗费。

0
1
分享到:
评论
2 楼 chriszeng87 2011-06-09  
Technoboy 写道
貌似有点问题

应该没有吧
1 楼 Technoboy 2011-06-06  
貌似有点问题

相关推荐

Global site tag (gtag.js) - Google Analytics