我正在尝试为在不同JBoss节点中运行的cron作业实现某种信号量.我正在尝试使用数据库(Oracle 11g)作为锁定机制,使用一个表来同步不同节点中的cron作业.表非常简单:
CREATE TABLE SYNCHRONIZED_CRON_JOB_TASK
(
ID NUMBER(10) NOT NULL,
CRONJOBTYPE VARCHAR2(255 Byte),
CREATIONDATE TIMESTAMP(6) NOT NULL,
RUNNING NUMBER(1)
);
ALTER TABLE SYNCHRONIZED_CRON_JOB_TASK
ADD CONSTRAINT PK_SYNCHRONIZED_CRON_JOB_TASK
PRIMARY KEY (ID);
Run Code Online (Sandbox Code Playgroud)
因此,当作业启动时,它会在表中搜索其cronjobtype的条目,并检查它是否已在运行.如果不是,则将条目设置运行标志更新为true.第一个选择是使用JPA CriteriaApi使用Hibernate和Pessimistic Lock进行的.
query.setLockMode(javax.persistence.LockModeType.PESSIMISTIC_WRITE);
Run Code Online (Sandbox Code Playgroud)
所有这些操作都是在一次交易中完成的.
当一个进程运行时,它所做的查询如下:
[Server:server-two] 10:38:00,049 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,048 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes
[Server:server-two] 10:38:00,049 INFO [stdout] (scheduler-2) Hibernate: select …Run Code Online (Sandbox Code Playgroud) 假设我有两个不同的线程,T1和T2,同时访问同一个数据库并从同一个表中获取数据.
现在在线程启动时,我需要从表中获取数据并将行存储到一个集合中,然后我将用它来执行其他工作.我不希望这两个线程能够处理相同的数据,因为它会导致重复(和长期)工作.更具体地说,这是一个企业应用程序,需要在启动时加载一些记录并将其存储在一个集合中以执行一些额外的工作.问题是在集群环境中,这可能导致两个不同的实例加载相同的数据,因此工作可能会重复.所以我希望行只能由一个实例加载一次.
我该如何避免这种情况?
我目前正在使用Hibernate和Oracle 10g.这些是我迄今为止的解决方案:
以编程方式锁定行.读取它的第一个将"锁定"列设置为true,但如果第一个线程在没有将行设置为"已处理"的情况下死亡,则很可能发生死锁.
使用悲观锁定.我尝试使用LockMode.UPGRADE,但这似乎没有帮助,因为我仍然能够同时从两个线程读取数据.
Run Code Online (Sandbox Code Playgroud)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; }
还有其他提示吗?我究竟做错了什么?
谢谢.