【发布时间】:2014-07-15 19:06:46
【问题描述】:
我有以下问题。我有 10 个线程创建插入数据库的对象。每个线程都有一个 ThreadLocal 和它自己的会话。所有对象在创建后都插入在一起。这些对象有一个标记为唯一的列。但是,我有一个问题,两个不同的线程可能会创建同一个对象。这种行为是需要的,但我不知道如何将它们插入到我的数据库中。
目前,每个线程都会查询所有插入数据库的对象,检查查询的对象是否存在,并将不存在的对象插入数据库。但是,由于在所有对象的查询中都可能不存在该对象,因此当我插入对象并且它们已经被另一个线程添加时,我会得到一个 ConstraintViolationException。但是,为每个对象执行数据库(或缓存)查询的性能会很差,因为我们试图在每个线程和分钟内添加 1000 个对象。如果我在每次插入后尝试刷新数据库,则会收到以下错误:Deadlock found when trying to get lock;尝试重启事务
所以我的问题是:我怎样才能插入同时具有来自不同线程的唯一约束的对象。
//编辑:目前我正在使用 Hibernate 和 MYSQL InnoDB
//Edit2:最后是我用来写单项的代码。
public class ItemWriterRunnable implements Callable<Object> {
private final ThreadLocal<Session> session = new ThreadLocal<Session>();
private Item item;
public ItemWriterRunnable(Item item) {
super();
this.item= item;
}
protected Session currentSession() {
Session s = this.session.get();
// Open a new Session, if this thread has none yet
if (s == null || !s.isOpen()) {
s = HibernateUtils.getSessionFactory().openSession();
// Store it in the ThreadLocal variable
this.session.set(s);
}
return s;
}
@Override
public Object call() throws Exception {
Session currentSession = currentSession();
try {
currentSession.beginTransaction();
currentSession.save(this.item);
currentSession.getTransaction().commit();
} catch (ConstraintViolationException e) {
currentSession.getTransaction().rollback();
} catch (RuntimeException e) {
currentSession.getTransaction().rollback();
} finally {
currentSession.close();
currentSession = null;
this.session.remove();
}
return null;
}
}
最好的问候, 安德烈
【问题讨论】:
-
您可以为每个对象生成一个线程,并在该线程中启动一个事务来写入该对象,而忽略任何由违反约束引起的错误。
-
这是否也允许多行插入? (例如,批量插入 10 个对象?每个对象一个事务听起来开销很大还是我错了?
-
不,每个插入一个对象。你别无选择——你不能在没有信号量的情况下进行检查/写入来锁定你的数据库并阻止该锁上的所有线程做任何工作,而且这只适用于单个 jvm——如果你分发你的事务例如,对于一个集群,你搞砸了。如果每次写入有多个对象,如果一个失败,它们都会失败。每次写入一个对象意味着只有一个对象失败。使用 check-then-write 模式时,线程开销没有锁定开销大。
-
好的,谢谢。我明天试试看;)
-
最后,我按照您描述的方式实现了它,并发性得到了真正的正确处理。谢谢你。但是,我仍在为性能而苦苦挣扎。我使用产生 25 个线程的 ExecutorService。每个线程都获得一个可调用对象,启动一个会话,启动一个事务,保存项目,提交事务并关闭事务。 (我添加了上面的代码)。有没有办法提高性能?
标签: java multithreading hibernate concurrency