如何多次进行随机运行的可运行运行?

时间:2010-06-26 21:04:40

标签: java concurrency time random

假设您有N个可运行的对象,并且每个对象都希望它们随机执行。一旦可运行对象执行了该时间段,您希望将其重新安排为另一个随机时间段运行。您希望能够多次为每个runnable执行此操作。

一旦runnable启动,它应该只是在一个无限循环中执行 - 也就是说runnable不知道它将运行多长时间。从runnable的角度来看,它将无限期地运行。

如何实现这一目标,理想情况下只使用标准Java API?如果这不可能实现,那么最接近的设计是什么?

3 个答案:

答案 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)

毕竟......你必须将TimerTaskTimer结合使用。我希望这是最后一部分:)!

你应该尝试这样的事情:

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放出并在执行完毕后将其放回,而不是使用简单列表。

此外,可能存在一些同步问题,但是根据您提供的信息,我认为这就足够了。

我希望它有所帮助。