项目中总是出现招标项目超投的情况,最开始总是觉得应该使用框架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的实时查询。
本文出自 “飞向阳光的鱼” 博客,请务必保留此出处http://bella02.blog.51cto.com/5546479/1763703
原文地址:http://bella02.blog.51cto.com/5546479/1763703