假设我有两个不同的线程 T1 和 T2,它们同时访问同一个数据库并从同一个表中获取数据。
现在,在线程启动时,我需要从表中获取数据并将行存储到集合中,然后我将使用该集合在其他地方执行一些工作。我不希望两个线程能够处理相同的数据,因为这会导致重复(且耗时)的工作。更具体地说,这是一个企业应用程序,需要在启动时加载一些记录并将其存储在集合中以完成一些额外的工作。问题是,在集群环境中,这可能会导致两个不同的实例加载相同的数据,因此工作可能会重复。所以我希望单个实例仅加载行一次。
我怎样才能避免这种情况呢?
我目前正在使用 Hibernate 和 Oracle 10g。这些是我到目前为止的解决方案:
public List<MyObject> getAllNtfFromDb() {
Session session = HibernateUtil.getOraclesessionfactory().openSession();
Query q = session.createQuery(
"from MyObject n where n.state = 'NEW'");
List<MyObject> list = (List<MyObject>) q.list();
for (int i=0; i<list.size(); i++)
session.lock(list.get(i), LockMode.UPGRADE);
return list;
}
还有其他提示吗?我究竟做错了什么?
Thanks.
你需要使用PESSIMISTIC_WRITE
在查询时:
Query q = session
.createQuery("from MyObject n where n.state = 'NEW'")
.setLockOptions(new LockOptions(LockMode.PESSIMISTIC_WRITE));
List<MyObject> list = (List<MyObject>) q.list();
锁定父对象就足够了。死锁不一定会发生。如果持有锁的线程没有在另一个线程超时等待之前释放锁,则可能会导致锁获取失败。
由于您使用的是 Oracle,因此这就是选择更新 https://docs.oracle.com/cd/E17952_01/refman-5.1-en/innodb-locking-reads.html works:
SELECT ... FOR UPDATE 锁定行和任何关联的索引
条目,就像您为这些行发出 UPDATE 语句一样。
其他事务被阻止更新这些行,无法执行
SELECT ... LOCK IN SHARE MODE,或从某些模式读取数据
事务隔离级别。一致的读取会忽略设置的任何锁
读取视图中存在的记录。 (旧版本的记录
无法锁定;它们是通过应用撤消日志来重建的
记录的内存副本。)
因此,如果 T1 获取了某些行的独占锁,则在 T1 提交或回滚之前,T2 将无法读取这些记录。如果 T2 使用READ_UNCOMMITTED http://www.oracle.com/technetwork/issue-archive/2010/10-jan/o65asktom-082389.html隔离级别,那么 T2 永远不会阻塞锁定记录,因为它只是使用撤消日志来重建数据,就像查询开始时一样。与 SQL 标准相反,Oracle READ_UNCOMMITTED 将:
为了提供一致或正确的答案,Oracle 数据库将
创建包含该行的块的副本,因为它在
查询开始...实际上,Oracle 数据库绕过了
修改后的数据——它读取周围的数据,并从撤消中重建它
(也称为回滚)段。一致且正确的答案
无需等待事务提交即可返回。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)