我正在使用jersey设计一个java web应用程序,其中我在mysqldb中有一个日期-时间字段"future_time"。我也是一个在同一个表中有一个状态字段设置为挂起的人。如果当前时间>=future_time,我想在数据库中将状态字段更新为completed。这就像我想安排一个活动。
其次,如果current_time<future_time,如果我更新了future_time值,我想重新安排上一个事件。
目前,每当我获取状态字段时,我都会检查它是否挂起,如果它挂起,那么我会检查future_time<=currenttime,如果是,那么我将db中的status字段更新为completed。但这里面有一个问题。这只会在我获取状态字段时更新它。因此,如果我在很长一段时间后(future_time之后)获取状态字段,那么它会在那一刻更新,从而在数据库中保存不正确的记录。
有人能给我一个更好的方法来实现这一点吗?
计算截止日期前的运行时间
使用JDBC 4.2及更高版本,可以使用现代java.time类检索日期时间。
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ); // Likely to be a UTC value.
获取当前时刻。
OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ) ;
计算运行时间。
Duration d = Duration.between( now , odt ) ;
安排事件
使用现代Executor框架,而不是传统的Timer
类。在Servlet或Jakarta.ee(JavaEE)环境中尤其如此。
定义你的执行人,特别是ScheduledExecutorService
。我们只需要一个线程就可以完成这项任务。
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
让执行器等待一定的时间,然后运行代码来检查数据库中的状态。
myExecutorService.schedule( // Tell the executor what you want to run, and when you want it run on your behalf in a background thread.
new Runnable() { … } , // Define task to be executed as a `Runnable`.
d.toSeconds() , // Amount of time to wait until execution. Use self-documenting code rather than a “magic number” such as `86400000`.
TimeUnit.SECONDS // Specify the granularity of time used in previous pair of arguments.
) // Returns a `ScheduledFuture` which you may want to cache.
您可以选择捕获此schedule
语句返回的ScheduledFuture
。然后,您可以监视完成状态。
在Runnable
中,从数据库中检索当前状态记录。如果有必要,安排另一次执行,使用新的当前时刻重新计算等待时间。
Servlet web容器
对于没有完整Jakarta.ee堆栈的Servlet web容器,如Tomcat或Jetty,适用上述讨论。
此外,您还希望编写一个实现ServletContextListener
接口的类,在该接口中您可以设置和拆除执行器服务。你需要优雅地拆除执行器服务,这样它的后台线程就不会在你的网络应用程序关闭后很长一段时间内愉快地继续运行。
雅加达EE
如果在像Glassfish这样成熟的Jakarta.ee堆栈中,这项工作要容易得多。
上面看到的遗嘱执行人服务可以是自动化的,可以代表您进行设置和拆除。使用JSR 236中定义的并发实用程序:JavaTM EE的并发实用工具。这已经被报道过很多次了,所以搜索Stack Overflow可以获得更多信息。
关于java.time
java.time框架构建在Java8及更高版本中。这些类取代了诸如java.util.Date
、Calendar
和&SimpleDateFormat
。
现在处于维护模式的JodaTime项目建议迁移到java.Time类。
要了解更多信息,请参阅Oracle教程。并在Stack Overflow中搜索许多示例和解释。规范是JSR310。
您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。不需要字符串,也不需要java.sql.*
类。
从哪里获得java.time类?
- Java SE 8、Java SE 9和Java SE 10,爪哇SE 11及更高版本-标准Java API的一部分,带有捆绑实现。
- Java9添加了一些小功能和修复程序
- Java SE 6和Java SE 7
- 大多数Java.time功能都是向后移植到Java 6&7英寸ThreeTen背包
- Android
- java.time类的Android捆绑包的后续版本
- 对于早期的Android(<26),ThreeTenABP项目适用于ThreeTen Backport(如上所述)。请参阅如何使用ThreeTenABP…
ThreeTen Extra项目通过附加类扩展java.time。这个项目是将来可能添加到java.time的试验场。您可以在这里找到一些有用的类,如Interval
、YearWeek
、YearQuarter
等等。
您可以使用ScheduledExecutorService
在特定的时间间隔、经过一些固定的延迟或在某个特定的时间点安排任务。
您可以创建ScheduledExecutorService
,如:
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
然后使用该服务来安排作业,如:
//scheduling at fixed time.
Future<String> resultFuture = executorService.schedule(new Callable<String>() {
@Override
public String call() throws Exception
{
//your code;
}
}, 1, TimeUnit.SECONDS);
//scheduling at fixed intervals.
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run()
{
//your code
}
}, 100, 450, TimeUnit.MILLISECONDS);
//scheduling at fixed delays.
executorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run()
{
//your code
}
}, 100, 150, TimeUnit.MILLISECONDS);
您也可以使用Java Timer Utility为将来安排任务,但Java规范表示更喜欢ScheduledExecutorService
。下面是一个使用Timer调度作业的示例代码。
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;
/**
* An example for Java Timer Utility.
*
* @author Karan Khanna.
*/
public class TimerExample
{
public static void scheduleTaskForFuture(final Date date)
{
Timer timer = new Timer("Timer");
TimerTask task = new TimerTask()
{
public void run()
{
System.out.println(
String
.format("Task performed on: %s and Thread's name: %s", date, Thread.currentThread().getName()));
timer.cancel();
}
};
timer.schedule(task, date);
}
public static void main(final String[] args)
{
Calendar calendar = new GregorianCalendar();
calendar.add(Calendar.MINUTE, 1);
TimerExample.scheduleTaskForFuture(calendar.getTime());
}
}
您可以更多地探索该实用程序来安排重复任务。此外,不要忘记在完成所需的工作后取消任务。
您也可以尝试其他框架,如Quartz和Spring调度。