and*_*lka 5 java simulation multithreading
我正在设计我的软件,以便我可以轻松地执行单元测试.我有一个IClock接口,其中包括其他方法
IClock#wait(TimeUnit timeUnit, long duration).此方法将暂停当前线程的timeUnit持续时间(即1秒).
IClock接口有两种实现方式:
SimulatedClock:有手动增加时钟存储时间的方法RealClock:时间通过参考自动增加 System.currentTimeMillis()这是以下的默认方法IClock#wait(...):
/**
* Locks current thread for specified time
*
* @param timeUnit
* @param dt
*/
default void wait(TimeUnit timeUnit, long dt)
{
Lock lock = new ReentrantLock();
scheduleIn(timeUnit, dt, lock::unlock);
lock.lock();
}
Run Code Online (Sandbox Code Playgroud)
我希望模拟单元测试工作的当前方式是
IClock#wait(...))SimulatedClock时间增加一毫秒.然而,真正发生的是:
IClock#wait()第一次没有调用,也要开始增加时间.所以,我需要做的是能够确定何时完成或阻止所有线程.虽然可以这样做Thread#getState(),但我宁愿使用更优雅且可以使用的解决方案ForkJoinPool.
package com.team2502.ezauton.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class SimulatedClock implements IClock
{
private long time = 0;
private List<Job> jobs = new ArrayList<>();
public SimulatedClock() {}
public void init()
{
init(System.currentTimeMillis());
}
public void init(long time)
{
setTime(time);
}
/**
* Add time in milliseconds
*
* @param dt millisecond increase
* @return The new time
*/
public long addTime(long dt)
{
setTime(getTime() + dt);
return getTime();
}
/**
* Adds time with units
*
* @param timeUnit
* @param value
*/
public void addTime(TimeUnit timeUnit, long value)
{
addTime(timeUnit.toMillis(value));
}
/**
* Add one millisecond and returns new value
*
* @return The new time
*/
public long incAndGet()
{
return addTime(1);
}
/**
* Increment a certain amount of times
*
* @param times
*/
public void incTimes(long times, long dt)
{
long init = getTime();
long totalDt = times * dt;
for(int i = 0; i < times; i++)
{
if(!jobs.isEmpty())
{
addTime(dt);
}
else
{
break;
}
}
setTime(init + totalDt);
}
/**
* Increment a certain amount of times
*
* @param times
* @return
*/
public void incTimes(long times)
{
incTimes(times, 1);
}
@Override
public long getTime()
{
return time;
}
public void setTime(long time)
{
jobs.removeIf(job -> {
if(job.getMillis() < time)
{
job.getRunnable().run();
return true;
}
return false;
});
this.time = time;
}
@Override
public void scheduleAt(long millis, Runnable runnable)
{
if(millis < getTime())
{
throw new IllegalArgumentException("You are scheduling a task for before the current time!");
}
jobs.add(new Job(millis, runnable));
}
private static class Job
{
private final long millis;
private final Runnable runnable;
public Job(long millis, Runnable runnable)
{
this.millis = millis;
this.runnable = runnable;
}
public long getMillis()
{
return millis;
}
public Runnable getRunnable()
{
return runnable;
}
}
}
Run Code Online (Sandbox Code Playgroud)
package com.team2502.ezauton.command;
import com.team2502.ezauton.utils.SimulatedClock;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
public class Simulation
{
private final SimulatedClock simulatedClock;
private List<IAction> actions = new ArrayList<>();
public Simulation()
{
simulatedClock = new SimulatedClock();
}
public SimulatedClock getSimulatedClock()
{
return simulatedClock;
}
public Simulation add(IAction action)
{
actions.add(action);
return this;
}
/**
* @param timeoutMillis Max millis
*/
public void run(long timeoutMillis)
{
simulatedClock.init();
actions.forEach(action -> new ThreadBuilder(action, simulatedClock).buildAndRun());
simulatedClock.incTimes(timeoutMillis);
// Need to wait until all threads are finished
if(!ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.SECONDS))
{
throw new RuntimeException("Simulator did not finish in a second.");
}
}
public void run(TimeUnit timeUnit, long value)
{
run(timeUnit.toMillis(value));
}
}
Run Code Online (Sandbox Code Playgroud)
@Test
public void testSimpleAction()
{
AtomicBoolean atomicBoolean = new AtomicBoolean(false);
Simulation simulation = new Simulation();
simulation.add(new DealyedAction((TimeUnit.SECONDS, 5) -> atomicBoolean.set(true)));
simulation.run(TimeUnit.SECONDS, 100);
Assert.assertTrue(atomicBoolean.get());
}
Run Code Online (Sandbox Code Playgroud)