假设您有N个可运行的对象,并且每个对象都希望它们随机执行。一旦可运行对象执行了该时间段,您希望将其重新安排为另一个随机时间段运行。您希望能够多次为每个runnable执行此操作。
一旦runnable启动,它应该只是在一个无限循环中执行 - 也就是说runnable不知道它将运行多长时间。从runnable的角度来看,它将无限期地运行。
如何实现这一目标,理想情况下只使用标准Java API?如果这不可能实现,那么最接近的设计是什么?
答案 0 :(得分:3)
您可能会发现这更简单。
ScheduledExecutorService ses = ...
Runnable runnable = ...
new RandomExecutor(ses, runnable, 10, 10);
new RandomExecutor(ses, runnable, 10, 10);
// run for a random length of time and wait for a random length of time, repeat.
public class RandomExecutor implements Runnable {
private static final Random rand = new Random();
private ScheduledExecutorService ses;
private Runnable runnable;
private int maxRun;
private int maxSleep;
public RandomExecutor(ScheduledExecutorService ses, Runnable runnable, int maxRun, int maxSleep) {
this.ses = ses;
this.runnable = runnable;
this.maxRun = maxRun;
this.maxSleep = maxSleep;
ses.execute(this);
}
@Override
public void run() {
long end = System.currentTimeMillis() + rand.nextInt(maxRun);
do {
runnable.run();
} while(end > System.currentTimeMillis());
ses.schedule(this, rand.nextInt(maxSleep)+1, TimeUnit.MILLISECONDS);
}
}
答案 1 :(得分:2)
毕竟......你必须将TimerTask与Timer结合使用。我希望这是最后一部分:)!
你应该尝试这样的事情:
public final class TaskManager
{
private Timer _timer;
private final ArrayList<Semaphore> _permits;
private final ExecutorService _threadPool;
public TaskManager(int numTasks)
{
_timer = new Timer()
_permits = new ArrayList<Semaphore>();
_threadPool = Executors.newFixedThreadPool(numTasks);
for(int i = 0; i < numTasks; ++i)
{
Semaphore available = new Semaphore(1);
_permits.add(available);
// execute the task
_threadPool.execute(new Runnable(){
public void run(){
// run the task
(new SampleTask(available)).run();
// schedule the task to be stopped after some delay
_timer.schedule(new TimerTask(){
public void run() {
// Stops the task
available.acquire();
}
}, /*SOME_RANDOM_DELAY*/;);
}
});
}
}
public void run()
{
while(true)
{
for(Semaphore available: _permits)
{
int delay = /*RANDOM_DELAY*/;
Semaphore permit = available;
// Allows the task to work
permit.release();
// Schedules when to stop the task
_timer.schedule(new TimerTask(){
public void run() {
// Stops the task
permit.acquire();
} }, delay);
// perhaps you should do something to ensure that you don't schedule the same permit twice...
}
}
}
}
public final class SampleTask extends Runnable {
private final Semaphore _available;
private final TaskManager _taskManager;
public SampleTask(Semaphore available)
{
_available= available;
}
// Implements the run method
public void run()
{
while(true)
{
// wait till I'm allowed to work
_available.acquire();
// pretend like I'm working
// release the semaphore when finished
_available.release();
}
}
}
答案 2 :(得分:1)
是的,我认为这是可能的。您只需要保留任务的截止日期,然后使用Timer
,您只需检查给定的TimerTask
是否应该继续运行,或者取消计时器。
这是一个完整的例子。它远非完美,但只是它应该如何运作的概念证明。
对于Runnable列表,您启动一个新的ExecuteTask
实例,该实例在内部知道它是否应该再次运行,或者它们是否已到达死线。
请注意,Runnables不知道它们是永久运行还是根本不运行。
在下面的代码中,我每秒重新安排一次,随机时间在10秒范围内,但你可以每毫秒重新安排一次,直到任何合理的时间为止。
例如:
Execute task = new ExecuteTask( new Runnable(){
public void run(){
System.out.println("Hi");
}
});
task.start(); // would run for "random" seconds....
我希望,我已经理解了你需要的东西。
import java.util.*;
import static java.lang.System.currentTimeMillis;
import static java.lang.System.out;
class ScheduledExecutionDemo {
public static void main( String [] args ) {
List<Runnable> runnables = Arrays.asList( new Runnable[]{
new Runnable(){ public void run(){ out.println("I'm the one");}},
new Runnable(){ public void run(){ out.println("I'm the two");}},
new Runnable(){ public void run(){ out.println("I'm the three");}},
new Runnable(){ public void run(){ out.println("I'm the four");}},
});
for( Runnable run : runnables ) {
new ExecuteTask( run ).start();
}
}
}
class ExecuteTask extends TimerTask {
// This map keeps track on when every task must finish.
// Every time a new instance is created it is stored here
// and every time it is scheduled again checks if it still have time.
private final static Map<Timer, Long> upTo = new HashMap<Timer, Long>();
private final static Random random = new Random();
private final Timer owner;
private final Runnable task;
public ExecuteTask( Runnable task ) {
this.owner = new Timer();
this.task = task;
upTo.put( owner, currentTimeMillis() + random.nextInt( 10 ) * 1000 );
}
public void start() {
owner.schedule( this, 0 , 1000 );
}
public void run() {
if( shouldRunAgain() ) {
task.run();
} else {
owner.cancel();
}
}
private boolean shouldRunAgain() {
return ExecuteTask.upTo.get( owner ) > currentTimeMillis();
}
}
通过这个概念验证,您可以使用队列并在执行时将runnable放出并在执行完毕后将其放回,而不是使用简单列表。
此外,可能存在一些同步问题,但是根据您提供的信息,我认为这就足够了。
我希望它有所帮助。