项目中总是出现招标项目超投的情况,最开始总是觉得应该使用框架Hibernate自带的并发策略中的乐观锁(version)解决问题,参考了很多网上的资料,也参考了Hibernate的帮助文档,由于对Hibernate乐观锁机制不了解,问题就一直没有解决。

    最近在看Java并发编程相关知识,了解了些许并发,线程,锁的知识。想到了这个问题,曾经使用Synchroized关键字时总是苦于无法获取同一个对象,导致解决方案无效。这次采用的方案是:创建了静态的HashMap<Integer,Lock>,初始化一定数量的对象(可结合服务器的性能来确认对象的数量),采用公平模式竞争锁,在处理业务数据。

package com;
import java.util.HashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class testMain {
/**
 * test类
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub
testOut testOut=new testOut();
outPut o =new outPut(testOut);
//模拟并发三个线程
new Thread(o,"first").start();
new Thread(o,"second").start();
new Thread(o,"thread").start();
}
}
/**
 * 线程类
 * @author linyan
 *
 */
class outPut implements Runnable{
private testOut testOut;
public outPut(testOut t){
testOut=t;
}
@Override
public void run() {
// TODO Auto-generated method stub
testOut.sysout(20);
}
}
/**
 * 业务处理方法
 * @author linyan
 *
 */
class testOut{
/**
 * 创建静态map
 * 初始化map对象
 */
public final static HashMap<Integer,Lock> map=new HashMap<Integer,Lock>();
static{
for(int i=0;i<2;i++){
//初始化公平锁,确保等待时间最久的线程获得锁
map.put(i, new ReentrantLock(true));
}
}
private int number=0;
public void sysout(int i){
System.out.println(Thread.currentThread().getName()+":"+i);
//模拟同一个招标项目的标的编号来争抢锁
int index=i%map.size();
try{
map.get(index).lock();
//TOOD  业务逻辑处理方法
number=number+i;
//输出当前获得线程的锁及请求参数
System.out.println(Thread.currentThread().getName()+":"+index);
//获得当前线程内查看到的活动线程数
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().activeCount());
//输出对应累加的数值
System.out.println(Thread.currentThread().getName()+"="+number);
}finally{
map.get(index).unlock();
}
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}

对应的输出结果:

thread:20
first:20
first:0
first:4
first=20
second:20
thread:0
thread:3
thread=40
second:0
second:2
second=60

    经过反复的测试,输出的结果都和预期一致,故判定这一解决方案有效。将解决方案放到具体项目上实施,结果仍然出现了超投问题,经过问题定位发现,在业务逻辑处理中业务逻辑的事务分割范围太宽,导致了数据不能立即入库,同时Hibernate的查询仍与缓存交互,不是实时查询数据库,导致继续超投的情况。

    事务范围切割问题很好解决,但是对Hibernate查询问题一直没有解决,为了解决问题,采用了临时方案:创建JDBC连接来实时查询。最终以牺牲了部分性能为代价,解决这个问题,最近在研究一下Hibernate查询方式,看是否能用Hibernate的实时查询。