一般EJB持久化实体的流程如下:
1.初始化上下文Context ctx
2.调用ctx的lookup方法获取具体的dao
3.调用dao的具体方法
由于多用户同时操作同一个对象,导致数据不同步的问题。这就是所谓多线程引发的问题。
简单地举例说明上面那个问题:
(银行帐号存取款问题)某一个银行帐号里边现有存款1万,假设两个人A,B同时操作这个帐号,A作存款100操作,B作取款200操作
在A存款的某一时刻,银行系统先读取存款1万后+100,还没来得及将总合1万1百块钱存入数据库,B就作取款操作,此时系统读取1万块,再作-200操作,B处理完成后,帐号总额应该是9800块钱。但在B处理完后,A继续执行存款操作(将1万1百块存入数据库,此时,数据库值从9800一下子变成了10100块)
我们知道JAVA用synchronized关键字作为同步排斥的,但前提是,同一个对象的所有锁,必须是指定同一个对象,否则加锁等于没锁。
经测试EJB每一次获取dao对象时,每一个对象都是不同的对象,所以不能简单地用调用对象所指向的锁(this或在方法前加synchronized),
public synchronized void fuck(){...} public void fuck2(){ synchronized(this){...} }
而应该使用静态锁。
JAVA当中,每一类对象在会有一份字节码保存于内存之中,有且只有一份。所以ejb当中,应当使用静态同步锁来排斥其他线程对同一个方法的访问,伪码如下:
class Fuck{
public void fuck3(){ synchronized(Fuck.class){...} }
}
我使用JUnit写了测试,用两个线程同时跑,两个线程跑时,都模拟了EJB创建DAO过程,并调用同一个DAO内同一个方法。
@Test
public void fuck() throws NamingException{
new Thread(new Runnable(){
@Override
public void run() {
try {
while(true){
fuckDao2 = (FuckDao)ctx.lookup("Fuck/remote");
fuckDao2.done("00000");
}
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
while(true){
fuckDao = (FuckDao)ctx.lookup("FuckImpl/remote");
fuckDao.done("22222");
}
}
####dao下同步方法的代码
public void done(String name) {
synchronized(FuckImpl.class){
int l = name.length();
for(int i=0;i<l;i++){
System.out.println(name.charAt(i));
}
}
}
经测试,以上方法能够解决EJB当中同一类DAO的不同实例同时调用DAO里同一个方法的排斥同步问题。