多线程

时间:2015-09-11 11:50:48

标签: java multithreading spring hibernate spring-mvc

我是Java和Hibernate的新手。

我已经实现了一个生成请求的功能。基于已保存的请求号。这是通过找到最大请求号来完成的。并将其递增1,然后再将其保存到数据库中。

但是我遇到多线程问题。当两个线程同时访问我的代码时,两者都生成相同的请求号。我的代码已经同步了。请提出一些解决方案。

synchronized (this.getClass()) {
        System.out.println("start");

        certRequest.setRequestNbr(generateRequestNumber(certInsuranceRequestAddRq.getAccountInfo().getAccountNumberId()));
        reqId = Utils.getUniqueId();
        certRequest.setRequestId(reqId);
        ItemIdInfo itemIdInfo = new ItemIdInfo();
        itemIdInfo.setInsurerId(certRequest.getRequestId());
        certRequest.setItemIdInfo(itemIdInfo);
        dao.insert(certRequest);
        addAccountRel();

        System.out.println("end");
}

以下是显示我同步的输出:

start
end
start
end

是否有一些Hibernate问题。 在Spring中使用transactional属性会影响我的Case中的代码提交吗?

我使用以下交易属性:

@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

编辑:聊天室中显示的generateRequestNumber()代码。

    public String generateRequestNumber(String accNumber) throws Exception {
        String requestNumber = null;
        if (accNumber != null) {
            String SQL_QUERY = "select CERTREQUEST.requestNbr from CertRequest as CERTREQUEST, "
                    + "CertActObjRel as certActObjRel where certActObjRel.certificateObjkeyId=CERTREQUEST.requestId "
                    + " and certActObjRel.certObjTypeCd=:certObjTypeCd "
                    + " and certActObjRel.certAccountId=:accNumber ";

            String[] parameterNames = {"certObjTypeCd", "accNumber"};
            Object[] parameterVaues = new Object[]
                    {
                            Constants.REQUEST_RELATION_CODE, accNumber
                    };
            List<?> resultSet = dao.executeNamedQuery(SQL_QUERY,
                    parameterNames, parameterVaues);

// List<?> resultSet = dao.retrieveTableData(SQL_QUERY); 
            if (resultSet != null && resultSet.size() > 0) {
                requestNumber = (String) resultSet.get(0);
            }
            int maxRequestNumber = -1;
            if (requestNumber != null && requestNumber.length() > 0) {
                maxRequestNumber = maxValue(resultSet.toArray());
                requestNumber = Integer.toString(maxRequestNumber + 1);
            } else {
                requestNumber = Integer.toString(1);
            }
            System.out.println("inside function request number" + requestNumber);
            return requestNumber;
        }
        return null;
    }

4 个答案:

答案 0 :(得分:1)

不要在通过getClass()获得的Class实例上进行同步。它可能有一些奇怪的副作用。见https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=43647087

例如使用:

synchronize(this) {
    // synchronized code
}

private synchronized void myMethod() {
    // synchronized code
}

要在对象实例上进行同步。

或者做:

private static final Object lock = new Object();

private void myMethod() {
    synchronize(lock) {
        // synchronized code
    }
}

像@diwakar建议的那样。这使用常量字段进行同步,以保证此代码在同一个锁上同步。

编辑:根据聊天信息,您使用SELECT获取最大requestNumber并增加代码中的值。然后在CertRequest上设置此值,然后通过DAO将其保存在数据库中。如果未提交此持久化操作(例如,通过使用@Transactional方法或其他方法),则另一个线程仍将看到旧的requestNumber值。所以你可以通过使代码进行事务处理来解决这个问题(如何依赖于你使用的框架等)。但我同意@ VA31的回答,该回答指出你应该使用数据库序列而不是增加代码中的值。您也可以考虑在CertRequest中使用自动入口字段,而不是序列,例如:

@GeneratedValue(strategy=GenerationType.AUTO)
private int requestNumber;

要从序列中获取下一个值,您可以查看this question

答案 1 :(得分:0)

优良作法是生成&#34;请求编号(唯一ID)&#34;使用数据库序列,这样您就不需要同步服务/ DAO方法。

答案 2 :(得分:0)

您在问题中提到了这些信息。

I have implemented a functionality where I generate request nos. based        on already saved request no. This is done by finding the maximum request no. and incrementing it by 1,and then again save i it to database.

初看起来,这似乎是多appserver代码引起的问题。线程在一个JVM(appserver)内同步。如果您使用多个appserver,则必须使用更强大的方法,通过使用服务器到服务器通信或通过批量分配请求no来为每个应用程序服务器执行此操作。

但是,如果您只使用一个appserver并且多个线程访问相同的代码,那么您可以锁定类的实例而不是类本身。

synchronized(this) { lastName = name; nameCount++; }

或者您可以使用类实例的私有锁

private Object lock = new Object();
  .
  .
  synchronized(lock) {
         System.out.println("start");

    certRequest.setRequestNbr(generateRequestNumber(certInsuranceRequestAddRq.getAccountInfo().getAccountNumberId()));
    reqId = Utils.getUniqueId();
    certRequest.setRequestId(reqId);
    ItemIdInfo itemIdInfo = new ItemIdInfo();
    itemIdInfo.setInsurerId(certRequest.getRequestId());
    certRequest.setItemIdInfo(itemIdInfo);
    dao.insert(certRequest);
    addAccountRel();

    System.out.println("end");
    }

但请确保在下一个线程访问新序列之前,通过新序列更新数据库以获取新序列。

答案 3 :(得分:-2)

第一件事:

为什么要在方法中获取线程?我不需要这里。

另外,有一件事;

你能尝试这样一次:

final static Object lock = new Object();

synchronized (lock)
{
 .....

}

我觉得你所说的对象是不同的,所以试试这个。