我想问一下什么是多线程Java应用程序的最佳解决方案,以确保所有线程同步访问db。例如,每个线程代表单独的事务,并首先检查db的值,然后根据答案必须插入或更新数据库中的某些字段(注意,插入和提交应用程序之间的注意事项正在进行其他处理)。但问题是另一个线程可能在同一个表上做同样的事情。更具体的例子。线程T1启动事务,然后检查表ENTITY_TABLE是否输入代码为'111'如果找到更新其日期,如果未找到则插入新条目,然后提交事务。现在假设线程T2完全相同。现在几乎没有问题:
1. T1和T2检查db并找不到任何内容,并且都插入相同的条目。
2. T1检查db,查找具有旧日期的条目,但是在提交T2时已经更新了更新的日期。
3.如果我们使用缓存并同步访问缓存,则会出现问题:T1获取锁定检查db和缓存,如果未找到则添加到缓存,释放锁定,提交。 T2做同样的事情,发现缓存中的条目将提交。但T1事务失败并被支持。现在T2处于糟糕的状态,因为它应该插入ENTITY_TABLE但不知道。
还有更多?
我正在努力创建简单的自定义缓存,同步和解决问题3.但我感兴趣的是,有一些更简单的解决方案吗?有没有人必须解决类似的问题?你是怎么做到的?
这应该主要通过配置所需的DB来处理 交易隔离 水平。然后在此基础上,您需要选择锁定策略(乐观 或者悲观)。
如果没有事务隔离,您将很难尝试仅在Java域中确保事务完整性。特别是考虑到即使数据库当前仅从您的Java应用程序访问,这可能在将来发生变化。
现在关于选择哪个隔离级别,从您的描述中可能看起来您需要最高的隔离级别, 序列化。然而,在实践中,这趋于
由于广泛的锁定,成为一个真正的性能生猪。因此,您可能需要重新评估您的要求,以便针对您的具体情况找到隔离和性能的最佳平衡。
这应该主要通过配置所需的DB来处理 交易隔离 水平。然后在此基础上,您需要选择锁定策略(乐观 或者悲观)。
如果没有事务隔离,您将很难尝试仅在Java域中确保事务完整性。特别是考虑到即使数据库当前仅从您的Java应用程序访问,这可能在将来发生变化。
现在关于选择哪个隔离级别,从您的描述中可能看起来您需要最高的隔离级别, 序列化。然而,在实践中,这趋于
由于广泛的锁定,成为一个真正的性能生猪。因此,您可能需要重新评估您的要求,以便针对您的具体情况找到隔离和性能的最佳平衡。
如果要从数据库中SQL SQL行,然后再更新同一行,则作为Java开发人员有2个选择。
用ROWLOCK选择,或者不管用什么
行锁语法适用于您的
特定的数据库。
选择行,进行处理,
就在你准备好之前
更新,再次选择行以查看
如果任何其他线程发生了变化
如果两个SELECTS返回相同的
值,更新。如果没有,扔一个
错误或再次处理。
在处理使用Sqllite数据库的多线程Java程序时,我遇到了这个问题。它使用文件锁定,所以我必须确保只有一个线程同时工作。
我基本上最终使用了synchronized。当ConnectionFactory返回数据库连接时,它还会返回一个锁定对象,在使用该连接时应锁定该对象。因此,您可以手动执行同步锁定,或者创建下面的类的子类,以便为您执行此操作:
/**
* Subclass this class and implement the persistInTransaction method to perform
* an update to the database.
*/
public abstract class DBOperationInTransaction {
protected Logger logger = Logger.getLogger(DBOperationInTransaction.class.getName());
public DBOperationInTransaction(ConnectionFactory connectionFactory) {
DBConnection con = null;
try {
con = connectionFactory.getConnection();
if(con == null) {
logger.log(Level.SEVERE, "Could not get db connection");
throw new RuntimException("Could not get db connection");
}
synchronized (con.activityLock) {
con.connection.setAutoCommit(false);
persistInTransaction(con.connection);
con.connection.commit();
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to persist data:", e);
throw new RuntimeException(e);
} finally {
if(con != null) {
//Close con.connection silently.
}
}
}
/**
* Method for persisting data within a transaction. If any SQLExceptions
* occur they are logged and the transaction is rolled back.
*
* In the scope of the method there is a logger object available that any
* errors/warnings besides sqlException that you want to log.
*
* @param con
* Connection ready for use, do not do any transaction handling
* on this object.
* @throws SQLException
* Any SQL exception that your code might throw. These errors
* are logged. Any exception will rollback the transaction.
*
*/
abstract protected void persistInTransaction(Connection con) throws SQLException;
}
和DBConnection结构:
final public class DBConnection {
public final Connection connection;
public final String activityLock;
public DBConnection(Connection connection, String activityLock) {
this.connection = connection;
this.activityLock = activityLock;
}
}
另外,我认为你必须在查询之前锁定表。这将强制您的线程顺序操作。然后你的线程应该为他们必须等待锁定的事实做好准备,当然,锁定获取可能会超时。这可能会给您的应用程序带来相当大的瓶颈,并且您的线程都必须排队等待数据库资源。
你面临的问题是 交易隔离。
好像你需要让每个线程在where子句中锁定相关的行,这需要可序列化的隔离。
你为什么要重新发明轮子?我建议使用OR映射器框架进行事务数据库访问(例如,像Hibernate或Eclipselink这样的JPA规范实现者)。您还可以添加Spring DAO来处理您的交易。然后你可以专注于业务逻辑,而不必担心这些低级别的东西。