public class 单例模式 {
/**
* 应用场景:
* 1. 系统访问人数
* 2. 系统内部事件
* 3. 系统操作日志
* 4. 系统出错日志
* 5. 系统配置访问接口
* ...
* [单例主要应用于一个类只有一个实例]
* 单例模式发展.
* *. 经典的单例模式
public class Singleton { private static Singleton instance = null; [懒汉式] protected Singleton() { // Exists only to defeat instantiation. } public static Singleton getInstance() { if(instance == null) { instance = new ClassicSingleton(); } return instance; } }
* 点评:这个类只到getInstance()方法被第一次调用时才被创建,但是一般的程序是应用于多线程环境下,这个类线程不安全
*
public synchronized static Singleton getInstance() { if(singleton == null) { singleton = new Singleton(); } logger.info("created singleton: " + singleton); return singleton; }
* 同步方法可以解决线程安全问题,但是同步方法开销会增大,为了整体系统的效率,尽量不要使用同步.
*
public class Singleton { private static Singleton instance = new Singleton();[饿汉式] } public static Singleton getInstance() { return Singleton.instance; }
* 点评:Bob Lee的写法,在类加载的时候实例就会被创建,只能通过getInstance()接口访问,能够实现线程同步
*
* Spring如何实现单例:
* ApplicationContext context=FileSystemXmlApplicationContext(String configLocation)
* ApplicationContext context=ClassPathXmlApplicationContext(String configLocation)
* 这两个都是声名配置文件的,但是加入的是同一个域.
* public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
}
* Synchronization monitor for the "refresh" and "destroy"
* private final Object startupShutdownMonitor = new Object();
* 在对象中声名一个最终对象进行锁定,这样Application一次就只能有一个方法进入.
* Struts如何实现单例:
public static Class applicationClass(String className, ClassLoader classLoader) throws ClassNotFoundException { if (classLoader == null) { // Look up the class loader to be used classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) { classLoader = RequestUtils.class.getClassLoader(); } } // Attempt to load the specified class return (classLoader.loadClass(className)); }
* 这是Struts得到类加载器并加载一个类.如果类加载器为null则使用当前线程的ClassLoader
* 有人可能会问如果是多线程不是会有问题了吗?我想说的是这是通过类的反映来返回类对象,并不是所有的对象都
* 需要使用多线程支持的,毕竟多线程要处理线程同步,开销太大.
* 现在再来介绍一个Struts如何应用多线程.[Action]
*
Return an Action
instance that will be used to process
* the current request, creating a new one if necessary.
protected Action processActionCreate(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException { String className = mapping.getType(); Action instance; synchronized (actions) { // Return any existing Action instance of this class instance = (Action) actions.get(className); if (instance != null) { if (log.isTraceEnabled()) { log.trace(" Returning existing Action instance"); } return (instance); } // Create and return a new Action instance if (log.isTraceEnabled()) { log.trace(" Creating new Action instance"); } try { instance = (Action) RequestUtils.applicationInstance(className); // Maybe we should propagate this exception // instead of returning null. } catch (Exception e) { log.error(getInternal().getMessage("actionCreate", mapping.getPath()), e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("actionCreate", mapping.getPath())); return (null); } actions.put(className, instance); if (instance.getServlet() == null) { instance.setServlet(this.servlet); } } return (instance); }
* 点评:actions是一个HashMap实例,这里使用同步代码块的方法synchronized(actions)[注意:同步代码块比同步方法开销小]
* 当一个请求过来时[线程],这里使用了同步,每一次都从actions中得到对象.如果不为空,则返回,如果为空则通过反映生成对象并返回.
* 这其实也属于单例注册.
* 上面介绍了Spring对应多个配置文件时用的同步,Struts的核心Action的同步,下面介绍一个Spring的核心Bean是如何实现单例的.
*
* Return the (raw) singleton object registered under the given name,
* creating and registering a new one if none registered yet.
public Object getSingleton(String beanName, ObjectFactory singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { // Re-check singleton cache within synchronized block. Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while the singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } beforeSingletonCreation(beanName);//singletonsCurrentlyInCreation,同步的集合,添加BeanName try { singletonObject = singletonFactory.getObject();//返回这个对象. } finally { afterSingletonCreation(beanName);//singletonsCurrentlyInCreation.同步的集合中,移除BeanName } addSingleton(beanName, singletonObject);//添加对象到singletonObjects } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
* 细心的读者会发现,Struts和Spring都是采用单例注册器的方式实现的.
* private final Map singletonObjects = CollectionFactory.createConcurrentMapIfPossible(16);
* 这里根据你的jdk版本判断使用哪种同步Map
* if JDKVersion>JAVA_15[或者包括edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap]
* new ConcurrentHashMap(initialCapacity)
* else
* Collections.synchronizedMap(new HashMap(initialCapacity));
* 如果单例注册器中没有此对象
* 1. beforeSingletonCreation(beanName); //监视创建对象前 * 2. singletonObject = singletonFactory.getObject();//返回对象 *singletonFactory.getObject();这是接口方法,这个接口由程序在调用的时候实现. *getSingleton(beanName, new ObjectFactory() { public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args);//创建方法就是在子类要重写的 } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } } }); * 3.afterSingletonCreation(beanName);//监视创建对象后 * 4.addSingleton(beanName, singletonObject);//添加对象到singletonObjects * 5.返回该对象. * 综上所述:在应用于系统资源时,为了提供同一接口,需要同步信息,但是Struts和Spring都使用了代码块同步,我们写程序的时候也要向 * 那些大师学习,尽量同步代码块. * */ }