单例模式与高并发
SpringMVC与struts2最大区别:SpringMVC使用单例、struts2使用原型(有可能struts2中对象使用后不销毁而只进行初始化变量再使用)
当某个单例对象中含有不具有并发性的对象(即并发会出错),为提高并发有三种方法:
1、线程绑定(只允许当前线程使用绑定对象)
2、创建对象池
3、加锁
单例与原型
单例在并发中可以有多个单例实例化对象。单例模式是只创建一个对象
单例:每个线程都需要使用实例化一个对象,但是对象的获取方式是不存在创建,存在直接使用。不安全是指一个线程修改了某个单例对象变量,而该对象不会销毁,会被其他线程使用从而造成该对象变量数据错误。
原型:每个线程直接创建自己使用的对象,使用完直接销毁。
单例是实例化不销毁被重复使用,原型使用后就销毁。所以避免单例对象使用中进行修改自身变量。
在单例对象中进行线程绑定对象,定义如下:
private ThreadLocal<Digester> tl = new ThreadLocal<Digester>(); public Digester getDigester() { Digester digester = (Digester) this.tl.get(); if (null == digester) { digester = super.getDigester(); this.tl.set(digester); } return digester; }
在单例对象使用时进行线程绑定一个对象,高并发时单例对象被大量创建,但每个单例只绑定了一个不支持并发的对象。
而在原型中高并下使用单例模式目的只创建一个对象:
publci class Singleton{ private static Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
高并发下会创建多个对象,而目的是只要一个实例对象,所以需要加锁进行同步
publci class Singleton{ private static Singleton instance = null; private Singleton(){} public static synchronized Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
上面方式很影响性能,所以采用下面方式:
publci class Singleton{ private static Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ synchronized(Singleton.class){ if(instance == null) instance = new Singleton(); } } return instance; } }
做了单例模式在多线程条件下的性能测试,结果表明我的想法是正确的:试图用单例来换取性能是不可行的,并发量较小(一般小于200),单例没有任何性能优势,也就没有使用必要,并发量较大(超过500),单例会引起类资源的争用,实例化速度急剧降低。这个测试说明:weblogic的最大线程=EJB实例的最大实例数=EJB中成员类(也就是业务类)的最大实例数,这几个东西是一条线下来的,是有内在关系的。
测试结果数据:
一 使用单例模式:
并发数量: 需要EJB实例数量 需要业务类实例数量 业务类实例化时间平均毫秒数
200 28 28 1
500 33 33 51
二 使用传统技术:
并发数量: 需要EJB实例数量 需要业务类实例数量 业务类实例化时间平均毫秒数
200 28 28 1
500 33 33 1
从以上结果可以看出:
第一:在多线程下,使用传统技术实例化类,根本不存在性能问题;
第二:在多线程下,使用单例模式实例化类,有比较严重的性能问题;