我有一种方法,其中一些数据库插入操作正在使用休眠进行,我希望它们是线程安全的。该方法正在获取参数中的一些数据,并且有时可能会在同一时间点使用相同的数据进行两次调用。
由于性能下降,我无法锁定这些表。任何人都可以建议将方法设为同步将解决问题吗?
我有一种方法,其中一些数据库插入操作正在使用休眠进行,我希望它们是线程安全的。该方法正在获取参数中的一些数据,并且有时可能会在同一时间点使用相同的数据进行两次调用。
由于性能下降,我无法锁定这些表。任何人都可以建议将方法设为同步将解决问题吗?
同步一个方法将确保它一次只能被一个线程访问。如果此方法是您写入数据库的唯一方法,那么是的,这将阻止两个线程同时写入。但是,您仍然必须处理具有相同数据的多个插入操作的事实。
您应该让 Hibernate 处理并发,这就是它的本意。不要假设 Hibernate 会锁定任何东西:它支持乐观事务正是为了这个目的。从上面的链接引用:
与高并发和高可扩展性一致的唯一方法是带有版本控制的乐观并发控制。版本检查使用版本号或时间戳来检测冲突更新并防止丢失更新。Hibernate 提供了三种可能的方法来编写使用乐观并发的应用程序代码。
数据库并发由事务处理。事务具有原子一致的隔离持久 (ACID) 属性。它们在同时访问数据库的程序之间提供隔离。在 Spring Framework 的 Hibernate DAO 模板中,有单行方法用于对数据库进行 CRUD 操作。当单独使用时,这些不需要通过方法同步。如果您需要将“您的方法”声明为具有特定传播设置、回滚设置、隔离设置的事务性,Spring 提供了声明式 (XML)、程序化和注释元数据驱动的事务管理。因此,在“您的方法”中,您可以进行多次保存、更新、删除等操作,并且 ORM 将确保使用您在元数据中提供的事务设置执行它。
另一个问题是线程必须锁定所有参与事务的对象。否则事务可能会失败,或者 ORM 将保留陈旧的数据。在另一种情况下,由于锁顺序,它可能导致死锁。我认为这才是真正回答你的问题。
对象 a 和 b 都有一个 Lock 类型的实例变量。布尔标志可用于指示事务的成功。如果失败,客户端代码可以重试相同的事务。
if (a.lock.tryLock()) {
try {
if (b.lock.tryLock()) {
try {
// persist or update object a and b
} finally {
b.lock.unlock();
}
}
} finally {
a.lock.unlock();
}
}
使用同步方法的问题在于它锁定了整个 Service 或 DAO 类,使其他服务方法对其他线程不可用。通过在对象上使用单独的锁,我们可以获得细粒度并发的优势。
选择最佳策略取决于架构,有时为了提高性能似乎更容易使用方法同步之类的技巧,但这是一种不好的方法。
毫无疑问,您应该使用事务,如果使用该策略您遇到性能问题,您应该优化您的数据库查询或数据库结构。
请记住,“同步”应该尽可能是原子的。
否。此方法可能使用了其他方法和对象,它们可能不是线程安全的。synchronized
使线程一次只能使用该方法的对象monitor
一次,因此它使线程安全成为相对于该对象的方法。
如果您确定所有其他线程仅使用此方法使用共享功能,那么使用它synchronized
可能就足够了。