单例模式-2 (Java单例对象同步问题探讨)
Java单例对象同步问题探讨![]()
![]()
级别: 初级
?
邓明 (dengming@cn.ibm.com), 高级信息系统工程师, IBM中国有限公司 商业咨询服务部
?
2003 年 12 月 10 日
?
在本文中,作者向大家讲述了Single Call 模式的原理,同时也介绍了Single Call 模式的实现问题。
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。正是由于这个特点,单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。
本文将探讨一下在多线程环境下,使用单例对象作配置信息管理时可能会带来的几个同步问题,并针对每个问题给出可选的解决办法。
public class GlobalConfig { private static GlobalConfig instance = null; private Vector properties = null; private GlobalConfig() { //Load configuration information from DB or file //Set values for properties } public static GlobalConfig getInstance() { if (instance == null) { instance = new GlobalConfig(); } return instance; } public Vector getProperties() { return properties; } }? ? public class GlobalConfig { private static GlobalConfig instance = null; private Vector properties = null; private GlobalConfig() { //Load configuration information from DB or file //Set values for properties } private static synchronized void syncInit() { if (instance == null) { instance = new GlobalConfig(); } } public static GlobalConfig getInstance() { if (instance == null) { syncInit(); } return instance; } public Vector getProperties() { return properties; } }? ? 这种处理方式虽然引入了同步代码,但是因为这段同步代码只会在最开始的时候执行一次或多次,所以对整个系统的性能不会有影响。 public class GlobalConfig {private static GlobalConfig instance;private Vector properties = null;private boolean isUpdating = false;private int readCount = 0;private GlobalConfig() { //Load configuration information from DB or file //Set values for properties}private static synchronized void syncInit() {if (instance == null) {instance = new GlobalConfig();}}public static GlobalConfig getInstance() {if (instance==null) {syncInit();}return instance;}public synchronized void update(String p_data) {syncUpdateIn();//Update properties}private synchronized void syncUpdateIn() {while (readCount > 0) {try {wait();} catch (Exception e) {}}}private synchronized void syncReadIn() {readCount++;}private synchronized void syncReadOut() {readCount--;notifyAll();}public Vector getProperties() {syncReadIn();//Process datasyncReadOut();return properties;} }? ? ? 2,采用"影子实例"的办法 具体说,就是在更新属性时,直接生成另一个单例对象实例,这个新生成的单例对象实例将从数据库或文件中读取最新的配置信息;然后将这些配置信息直接赋值给旧单例对象的属性。如下面代码所示。 ? ? 注意:在更新方法中,通过生成新的GlobalConfig的实例,从文件或数据库中得到最新配置信息,并存放到properties属性中。 上面两个方法比较起来,第二个方法更好,首先,编程更简单;其次,没有那么多的同步操作,对性能的影响也不大。 邓明:IBM中国有限公司 BCS 高级信息系统工程师。![]()
public class GlobalConfig { private static GlobalConfig instance = null; private Vector properties = null; private GlobalConfig() { //Load configuration information from DB or file //Set values for properties } private static synchronized void syncInit() { if (instance = null) { instance = new GlobalConfig(); } } public static GlobalConfig getInstance() { if (instance = null) { syncInit(); } return instance; } public Vector getProperties() { return properties; } public void updateProperties() { //Load updated configuration information by new a GlobalConfig object GlobalConfig shadow = new GlobalConfig(); properties = shadow.getProperties(); } }?![]()