【问题标题】:Data commit issue in multithreading多线程中的数据提交问题
【发布时间】:2015-12-07 23:29:03
【问题描述】:

我是 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

这是一些休眠问题。 Spring中事务属性的使用会影响我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;
    }

【问题讨论】:

  • 你在用这个方法中创建的“线程”对象做什么?找不到任何参考资料。这没有任何意义。
  • 如果您是 Java 新手,您不应该使用线程或 Hibernate。即使是有经验的开发人员也很难正确编写多线程代码。我怀疑您认为需要 Hibernate 的任何事情都不能通过 JDBC 做得更好或更好。重新考虑一下。
  • @VA31 线程对象仅用于控制台上的显示目的,以便了解哪个线程正在访问代码。它没有其他用途。
  • 您没有正确使用 Spring。这里没有很好的理由使用线程。让它变得简单:使用来自客户端的同步请求并阻塞直到响应返回。

标签: java multithreading spring hibernate spring-mvc


【解决方案1】:

使用 DATABASE SEQUENCE 生成“请求号(唯一 ID)”是一种很好的做法,这样您就不需要同步您的服务/DAO 方法。

【讨论】:

  • 有人对此投了反对票,但未提及原因。请问可以知道原因吗?
  • 这可能是这个问题的最佳解决方案。
【解决方案2】:

第一件事:

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

还有一件事;

你可以这样尝试一次吗:

final static Object lock = new Object();

synchronized (lock)
{
 .....

}

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

【讨论】:

  • 我的方法是按照你的建议使用“lock”同步的,但它仍然不能解决我的问题。对于两个线程,我仍然收到相同的请求。可以过来聊聊吗?
  • 如果可能的话,您能否发布整个代码。你怎么称呼这个方法?我想看看..
  • 嗨@diwakar,如果想查看确切的代码,你能来聊天吗?这确实是一个很长的代码,如果想看到确切的流程会很长
【解决方案3】:

不要在通过 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

【讨论】:

  • 我的方法是按照你的建议使用“lock”同步的,但它仍然没有解决我的问题。我仍然收到相同的请求。可以过来聊聊吗?
  • 我和你在聊天室,但你没有回应。
  • 我已经回答了你的问题,但还没有回复。
  • 老兄,已经两天了。
【解决方案4】:

您在问题中提到了此信息。

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.

乍一看,这似乎是由多应用服务器代码引起的问题。线程在一个 JVM(应用服务器)内同步。如果您使用多个应用服务器,那么您必须通过使用服务器到服务器通信或通过将请求号批量分配给每个应用服务器来使用更强大的方法来做不同的事情。

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

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");
    }

但请确保您的数据库在下一个线程访问它以获取新序列之前由新序列号更新。

【讨论】:

  • 嗨@Akshataprakash Sharma,我已经尝试过你的建议,但问题是我的数据库在下一个线程访问我的代码之前没有得到更新。这只是我的主要问题
  • 嗨@pkn1230,我了解您面临的问题。恐怕您在抽象方法“generateRequestNumber”中编写的代码不是线程安全的。检查一下[docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/…。这里它说休眠会话不是线程安全的。那么您可以尝试在事务中通过 session.doWork 对 DB 进行更新吗?希望对您有所帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
  • 1970-01-01
相关资源
最近更新 更多