Singleton容器
我们的代码中大量充斥着单例的实现,单例有他的优点,也有他的缺点,
优点:
1 对象只用创建一次,既能减少创建对象的开销也能减少GC的开销,尤其是各种需要解析XML的view中,使用单例能得到性能提升。
2 单例里的数据方便存取
缺点:
1 单例不能被回收,如果里面有List或map,系统注销时一定要clear
2 单例的属性存取需要同步,才能保证多线程下安全访问。
在我们的系统中充斥着大量的单例实现,如下:
1 基于饿汉模式的(类加载时就创建对象):
public class CounterManager { private static CounterManager INSTANCE = new CounterManager(); .... private CounterManager() { ... } public static CounterManager getInstance() { return INSTANCE; } }
2 基于DCL的懒汉模式的(第一次调用时创建对象):
public class SusimTransferView extends AbstractContentView{ ... private static volatile SusimTransferView INSTANCE = null; private SusimTransferView() { } public static SusimTransferView getInstance() { if(INSTANCE == null) { synchronized (SusimTransferView.class) { if(INSTANCE == null) { INSTANCE = new SusimTransferView(); } } } return INSTANCE; }
当前实现单例不只这两种方式,但是不管以哪种方式,只要是有类自身来显现单例的,都需要私有构造函数,编写
大量雷同的代码,实际上如果把单例的实现委托给一个专门的类管理,既能消除重复的代码,也方便对所有的单例
的生命周期进行管理,只要我们访问这些对象都从委托类中获取,我们就不需要被委托类必须编写私有的构造函数,
工厂模式正符合委托类的特征,通过工厂类,我可以方便的生产我们需要的对象,在单例和非单例直接切换也很容
易,并且能方便的管理所有对象的生命周期,这点在spring得到经典的实现,但是我们的系统中,系统注销时,只
需要清除某些对象,这点不方便用spring来实现,我参考spring写了一个BeanFactory类,这个类能方便的创建
单例对象,且自动管理单例对象的生命周期,系统注销或退出时,自动销毁所有的单例,唯一的要求就是需要被管
理的bean符合javabean规范,至少有一个无参构造函数,代码如下
/** * 实例工厂主要是提供单例对象 * 自动管理对象的生命周期 * * @author CZP * */ public class BeanFactory implements SystemEventListener{ private ConcurrentHashMap<String, Object> cache; private static final BeanFactory INSTANCE = new BeanFactory(); private BeanFactory() { cache = new ConcurrentHashMap<String, Object>(); } /** * 单例,可以重复注册到系统中 * @return */ public static BeanFactory getInstance() { return INSTANCE; } /** * 获取单例是线程安全的 * * @param cls * @return */ public Object getSingletonObject(Class<?> cls) { try { cache.putIfAbsent(cls.getSimpleName(), cls.newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return cache.get(cls.getSimpleName()); } /** * 系统注销或系统退出时清空所有的bean * */ @Override public void onSystemStatusChange(SystemEvent event)throws SscInterruptedException { if (event.getEventType().equalsIgnoreCase( SystemEventConstants.EVENT_LOGOUT) || event.getEventType().equalsIgnoreCase(SystemEventConstants.EVENT_SHUTDOWN)) { cache.clear(); AppParser.getSystemStatusManager().removeSystemEventListener(this); } } }
要获取单例非常简单,进行如下调用即可:
VPNManagementView view = (VPNManagementView)BeanFactory.getInstance().getSingletonObject(VPNManagementView.class);
之后BeanFactory 会自动管理这个bean的生命周期,注销或退出时,自动销毁该bean,实现
bean创建bean销毁与bean使用的解耦,调用者只需要拿到这个bean,进行相应的业务调用即可。
这个getSingletonObject没有加锁,能实现高效的调用,但是有人会怀疑这个是不是线程安全的
我们进行以下测试,起5个线程同时调用一个类:
public static void main(String[] args) { ExecutorService s = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { s.execute(new Runnable() { @Override public void run() { VPNManagementView view = (VPNManagementView)BeanFactory.getInstance().getSingletonObject(VPNManagementView.class); System.out.println("["+Thread.currentThread().getName()+"]"+ view); } }); } s.shutdownNow(); }
输出如下:
<nowiki>
[pool-1-thread-4]com.shjv.tdscdma.omc.client.opm.terminal.vpn.view.VPNManagementView@47a0d4
[pool-1-thread-3]com.shjv.tdscdma.omc.client.opm.terminal.vpn.view.VPNManagementView@47a0d4
[pool-1-thread-2]com.shjv.tdscdma.omc.client.opm.terminal.vpn.view.VPNManagementView@47a0d4
[pool-1-thread-5]com.shjv.tdscdma.omc.client.opm.terminal.vpn.view.VPNManagementView@47a0d4
[pool-1-thread-1]com.shjv.tdscdma.omc.client.opm.terminal.vpn.view.VPNManagementView@47a0d4
</nowiki>
可以看出,5个线程取到的都是同一个对象,说明这个方法是线程安全的,这个得益于ConcurrentHashMap的实现。