线程阻塞的问题
关键字:Spring, 单例,同步,线程阻塞,粒度
现象:某大型保险公司的OA应用(大集中模式,用户有2万多人,8千多个组织)在200多用户在线时,竟然发生了weblogic实例挂起,分析dump文件,发现有一个流程提交的线程发生意外,一直占有着一个对象的锁,因此它竟然阻塞了200多个线程,而所有的线程都阻塞在一个叫A的对象上,而这些被阻塞的线程还是处理不同的任务(就是说调用不同的方法),然后马上去看A.java的代码,发现这个类的所有方法都加了synchronized关键字,即都进行了同步,代码示例如下:
public class AImpl{
public void synchronized test1(){
}
public void synchronized test2(){
}
public void synchronized test3(){
}
}
public class BImpl{
private A a;
public void setA(A a){
this.a = a;
}
public void b_test1(){
a.test1();
}
pubic void b_test2(){
a.test2();
}
public void b_test3(){
a.test3();
}
}
问题出来了,有一个线程在执行b_test1()方法,而线程2在执行b_test2()方法,线程3在执行b_test3()方法,结果后面2个线程都被线程1阻塞了,等待它释放持有的a对象的这把锁,为什么会这样呢?而看AImpl.java这个类,其实作者的目的只是希望test1、test2、test3这三个方法在同一个时刻各自只有一个线程执行(即test1在同一个时刻只能有一个线程执行,其它要执行test1的线程只能排队,而test1、test2、test3三个方法是可以并发执行的),但是真实的现象是:test1、test2、test3三个方法都在排对了!也就是说test2、test3两个方法都在等待对象a的锁,而a的锁被执行test1方法的线程所持有了,为什么会这样呢,单例!突然想到了Spring的bean在默认情况下都是单态的,也就是说a对象在整个虚拟机都是唯一的!因此AImpl.java中的所有互不相干的方法都只能串行执行了!oh,my god!
解决办法:
1、 将AImpl.java拆分,也就说把test1、test2、test3三个方法分拆到三个类中去,这样就会有各自的实例,因此不再发生test1、test2、test3三个方法互相排队的事情发生
2、 利用一个小技巧,即我们不动AImpl.java的代码,只是在Spring的xxx.bean.xml的配置文件中,分别为test1、test2、test3三个方法配置三个实例就可以了(即a1,a2,a3),然后修改BImpl.java如下:
public class BImpl{
private A a1;
private A a2;
private A a3;
public void setA1(A a1){
this.a1 = a1;
}
public void setA2(A a2){
this.a2 = a2;
}
public void setA3(A a3){
this.a3 = a3;
}
public void b_test1(){
a1.test1();
}
pubic void b_test2(){
a2.test2();
}
public void b_test3(){
a3.test3();
}
}
总结:解决问题的本质在于一定要明白synchronized关键字锁住的是实例对象,因此问题的发生和解决都是基于它的