思路:建一个记录任务状态的表,通过去抢占表中的状态来获取执行定时任务的权限
直接上代码:
1,建一个表如下
create table XXJOB_STATUS ( id VARCHAR2(32) default sys_guid() not null, jobname VARCHAR2(40), status NUMBER(11) default '0', locktime VARCHAR2(80), bz VARCHAR2(40) )
2,所有定时器格式如下
public class XXJob extends Task { private static final String jobname = "XXJOBNAME"; public void execute(TaskExecutionContext content) throws RuntimeException { if(!isRun()){ if(lock()){ //执行定时任务 ************************* ************************* unlock(); }else{ //job被占用 } }else{ //======job被占用 } //end job } private boolean isRun(){ return Db.queryInt("select status from xx_job_status where jobname = ? ",jobname).intValue()==1; } private boolean lock(){ return Db.update("update xx_job_status set status = 1,locktime = ? where status = 0 and jobname= ?",DateFormatUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"),jobname)==1; } private boolean unlock(){ return Db.update("update xx_job_status set status = 0,locktime = '' where status = 1 and jobname= ?",jobname)==1; } }
1:isRun()、lock()、unlock() 都用 Db.tx(...) 开启事务
2:lock() 方法的抢占需要引入一个 locker 字段,用于标识是否是当前线程抢到了,具体办法如下:
a:添加一个字段名叫 locker,默认值为 null
b:locker 字段的值通过 StrKit.getRandomUUID() 生成一个 UUID 值,由于 UUID 是全球唯一的,所以可以确保不可能重复。而当前你的 lock() 方法在并发高的时候,多个线程很可能都返回 true
c:改进 lock() 内部代码逻辑,大致如下:
String UUID = StrKit.getRandomUUID();
// 该 sql 只更新 locker 字段,也就是先只去抢占锁,而不能做其它任何事情
String sql = "update task locker = ? where locker is NULL";
int n = Db.update(sql, UUID);
// n 大于 0 表示 update 成功,但不能保证是当前线程抢到的该 task
if (n > 0) {
// 通过前面生成的 UUID 去查询,查到了才能证明真的是当前线程抢占到了该 task
Record task = Db.findFirst("select * from task where locker = ?", UUID);
if (task != null) {
这里再对 task 进行处理,例如更新 task 表的其它字段
return true; // lock() 方法不建议返回 boolean 值,而是返回 task 对象,便于上层调用者使用,用完以后还可以方便置回状态
}
}
上面的改进思路主要是用一个 locker 字段让多线程先抢占锁,抢到以后再进行后续的操作
注意,上述示例代码用到了 task 表以及 locker 字段,与你给出来的代码中用的 xx_job_status 并不相同