java - 如何使用 Timers 使用动态计时器执行技能效果?
问题描述
目前,我正在使用计时器来执行我的技能效果。它会在所有技能效果下每 3 秒进行一次心跳。
这是我的代码:
public class EffectServiceImpl extends AbsServiceAdaptor<EffectConfig> {
private static EffectServiceImpl instance;
private FastList<Monster> existsEffectMonsterList;
private Timer timer;
public static EffectServiceImpl getInstance() {
if(null == instance) {
instance = new EffectServiceImpl();
}
return instance;
}
private EffectServiceImpl() {
existsEffectMonsterList = new FastList<Monster>();
timer = new Timer();
timer.schedule(new MonsterEffectCheckTask(), 10000, 3000); // Heartbeat every 3 seconds
}
public class MonsterEffectCheckTask extends TimerTask {
@Override
public void run() {
if(existsEffectMonsterList.size() > 0) {
Monster monster;
Effect effect;
for(int i = 0; i < existsEffectMonsterList.size();) {
monster = existsEffectMonsterList.get(i);
if(monster.effectList.size() > 0) {
for(int j = 0; j < monster.effectList.size();) {
try {
effect = monster.effectList.get(j);
if(effect.heartbeat(monster)) {
j++;
}
}
catch(Exception e) {
e.printStackTrace();
break;
}
}
}
if(monster.effectList.size() == 0) {
existsEffectMonsterList.remove(i);
}
else {
i++;
}
}
}
}
}
}
但是,我不希望所有技能效果都进行 3 秒的心跳。会有小于3秒或大于3秒(即动态周期)的心跳技能。
所以我将时间段更改timer.schedule
为 1:
...
timer.schedule(new MonsterEffectCheckTask(), 10000, 1);
...
然后添加Thread.sleep
到 TimerTask:
...
if(monster.effectList.size() > 0) {
for(int j = 0; j < monster.effectList.size();) {
try {
effect = monster.effectList.get(j);
if(effect.heartbeat(monster)) {
j++;
}
Thread.sleep(effect.execTime); // This is dynamic time, each effect has an `execTime`. Code `public int execTime;`
}
catch(InterruptedException e) {
e.printStackTrace();
}
catch(Exception e) {
e.printStackTrace();
break;
}
}
}
...
如果是上面的代码,我应该:使用schedule
还是scheduleAtFixedRate
其他?Thread.sleep
如果想“暂停”计时器,是否有任何解决方案可以替换?我应该设置什么delay
(period
我知道如果我将它设置为 0 我会得到一个IllegalArgumentException
,但如果设置为 1 太短了)?我希望在“暂停”时间到期后立即产生心跳的效果。此外,可以有许多效果相同的心跳,但每个效果的“暂停”时间并不相同。
我会很感激你的回答。
解决方案
执行者框架
Timer
现在通常被 Executors 框架过时。请参阅Oracle 的教程。
将您的任务定义为Runnable
对象Callable
。
使用由适当数量的线程支持的计划执行器服务。然后安排每个任务以您选择的时间间隔重复运行。
要检查或取消每个任务,请捕获ScheduledFuture
返回给您的对象。
如果您想改变任务执行之间的时间间隔,请通过其ScheduledFuture
对象取消任务。然后使用执行器服务重新安排新的时间间隔。
始终在您的应用程序结束之前关闭执行程序服务。否则后备线程池可能会像僵尸一样无限期地继续下去。
并包装您的任务代码以捕获任何不经意间冒出的异常。否则,未来的工作将默默地停止。
所有这些都在 Stack Overflow 上多次介绍过。所以我很简短。搜索以了解更多信息。下一次,在发布之前彻底搜索 Stack Overflow。
示例代码
我制作了一个示例应用程序来展示使用 executors 框架来更简单但更有效地管理后台线程工作的基础知识。
我们定义了一个Monster
接口,有两个实现类Demon
& Witch
。我创建了这些类的一些对象。这些类带有一个actOut
方法。我们的后台任务将是运行此方法。
对于每个现有Monster
实例,我们定义一个Runnable
简单地调用该Monster::actOut
实例上的方法的方法。我们通过一个对象安排每个可运行对象每隔几秒运行一次ScheduledExecutorService
。该执行器服务返回一个ScheduledFuture
对象来跟踪我们安排的每个可运行文件。我们将这些未来对象收集到一个列表中Demon
和一个列表中Witch
。
我们让它运行一分钟。在那段时间过去之后,我们改变Witch
任务的节奏。我们取消他们当前的任务,然后安排一个新的可运行对象以延长执行之间的间隔。我们让它再运行一分钟。在那之后,我们关闭了应用程序。
(顺便说一句,我们可以Runnable
在重新调度时回收现有对象而不是实例化新对象。根据您的需要做任何有意义的事情。)
此代码使用 Java 15 中预览的一些即将推出的功能,例如密封类型和记录。但这些对这里的概念并不重要;您可以使用以前版本的 Java 轻松地重新编写此代码。
样品运行
示例运行:
---------| Starting |--------------------------------------------
This monster Demon named Crowley buys a soul at 2020-12-08T02:32:39Z
This monster Demon named Barthamus buys a soul at 2020-12-08T02:32:39Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:32:41Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:32:41Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:32:46Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:32:46Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:32:51Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:32:51Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:32:56Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:32:56Z
This monster Demon named Crowley buys a soul at 2020-12-08T02:32:59Z
This monster Demon named Barthamus buys a soul at 2020-12-08T02:32:59Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:33:01Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:33:01Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:33:06Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:33:06Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:33:11Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:33:11Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:33:16Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:33:16Z
This monster Demon named Crowley buys a soul at 2020-12-08T02:33:19Z
This monster Demon named Barthamus buys a soul at 2020-12-08T02:33:19Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:33:21Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:33:21Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:33:26Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:33:26Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:33:31Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:33:31Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:33:36Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:33:36Z
---------| Changing speeds |--------------------------------------------
This monster Witch named Rowena casts a spell at 2020-12-08T02:33:38Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:33:38Z
This monster Demon named Crowley buys a soul at 2020-12-08T02:33:39Z
This monster Demon named Barthamus buys a soul at 2020-12-08T02:33:39Z
This monster Demon named Crowley buys a soul at 2020-12-08T02:33:59Z
This monster Demon named Barthamus buys a soul at 2020-12-08T02:33:59Z
This monster Witch named Rowena casts a spell at 2020-12-08T02:34:18Z
This monster Witch named Tasha casts a spell at 2020-12-08T02:34:18Z
This monster Demon named Crowley buys a soul at 2020-12-08T02:34:19Z
This monster Demon named Barthamus buys a soul at 2020-12-08T02:34:19Z
---------| Ending |--------------------------------------------
源代码
我们有四个班级:
Monster.java
(我们的业务对象的接口)Demon.java
(具体类)Witch.java
(具体类)
MonsterMash.java
(应用类)
代码:
package work.basil.example;
public sealed interface Monster
permits Demon, Witch
{
String name ( ); // This getter method implemented implicitly by `record` in our concrete classes `Demon` & `Witch`.
public void actOut ( );
}
package work.basil.example;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public record Demon(String name) implements Monster
{
@Override
public void actOut ( )
{
System.out.println( "This monster " + this.getClass().getSimpleName() + " named " + this.name + " buys a soul at " + Instant.now().truncatedTo( ChronoUnit.SECONDS ) );
}
}
package work.basil.example;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public record Witch(String name) implements Monster
{
@Override
public void actOut ( )
{
System.out.println( "This monster " + this.getClass().getSimpleName() + " named " + this.name + " casts a spell at " + Instant.now().truncatedTo( ChronoUnit.SECONDS ) );
}
}
package work.basil.example;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* Example for showing a change in cadence to scheduled tasks.
*/
public class MonsterMash
{
public static void main ( String[] args )
{
System.out.println( "Hello World!" );
MonsterMash app = new MonsterMash();
app.demo();
}
private void demo ( )
{
// Instantiate Monster objects.
List < Monster > monsters = List.of(
new Demon( "Crowley" ) ,
new Demon( "Barthamus" ) ,
new Witch( "Rowena" ) ,
new Witch( "Tasha" )
);
System.out.println( "monsters = " + monsters );
// Schedule each monster to do some work periodically.
System.out.println( "---------| Starting |--------------------------------------------" );
ScheduledExecutorService ses = null;
try
{
ses = Executors.newSingleThreadScheduledExecutor();
final List < ScheduledFuture > demonFutures = new ArrayList <>();
final List < ScheduledFuture > witchFutures = new ArrayList <>();
for ( Monster monster : monsters )
{
Runnable runnable = monster :: actOut; // Method reference used here for shorter syntax.
// Remember these futures.
if ( monster instanceof Demon )
{
ScheduledFuture scheduledFuture = ses.scheduleAtFixedRate( runnable , 1 , 20 , TimeUnit.SECONDS );
demonFutures.add( scheduledFuture );
}
if ( monster instanceof Witch )
{
ScheduledFuture scheduledFuture = ses.scheduleAtFixedRate( runnable , 3 , 5 , TimeUnit.SECONDS );
witchFutures.add( scheduledFuture );
}
}
// Wait a while to let the scheduled tasks do their work on background thread(s).
// Then alter the cadence of the tasks’ execution.
try
{
Thread.sleep( TimeUnit.MINUTES.toMillis( 1 ) );
System.out.println( "---------| Changing speeds |--------------------------------------------" );
for ( ScheduledFuture witchFuture : witchFutures )
{
witchFuture.cancel( false );
}
witchFutures.clear();
for ( Monster monster : monsters )
{
if ( monster instanceof Witch )
{
Runnable runnable = monster :: actOut;
ScheduledFuture scheduledFuture = ses.scheduleAtFixedRate( runnable , 0 , 40 , TimeUnit.SECONDS );
witchFutures.add( scheduledFuture );
}
}
Thread.sleep( TimeUnit.MINUTES.toMillis( 1 ) );
System.out.println( "---------| Ending |--------------------------------------------" );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
}
finally
{
if ( Objects.nonNull( ses ) ) { ses.shutdown(); }
}
}
}
推荐阅读
- bixby - 小区区域或小区卡主要和次要
- mysql - MySQL 和 Keycloak 设置
- java - 如何仅捕获异常类型
- gulp - 如何替换 gulp 中的几行
- html - Bootstrap 弹性柱
- mongodb - 在 rust mongo 中生成 ObjectId
- javascript - 向下滚动页面时,HTML5画布上的地图动画在路径中移动
- javascript - How to select closet element in jQuery and fire change event to file input?
- vb.net - “运行”功能的问题
- c++ - fscanf 返回无意义的值