Как заставить N runnables работать в течение случайных периодов времени, несколько раз? - PullRequest
3 голосов
/ 27 июня 2010

Допустим, у вас есть N запущенных объектов, и для каждого вы хотите, чтобы они выполнялись в течение случайных периодов времени. Как только исполняемый объект выполняется в течение этого периода времени, вы хотите перенести его на другой случайный период времени. Вы хотите иметь возможность сделать это для каждого из runnables, несколько раз.

Как только запускаемый файл запускается, он должен просто выполнять что-то в неопределенном цикле - то есть этот исполняемый файл не должен знать, как долго он будет работать. С точки зрения запуска, он будет работать бесконечно.

Как этого достичь, в идеале используя только стандартный Java API? Если это невозможно сделать как есть, какой альтернативный дизайн будет наиболее близким?

Ответы [ 3 ]

3 голосов
/ 27 июня 2010

Вы можете найти это проще.

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);
    }
}
2 голосов
/ 27 июня 2010

В конце концов .. вы должны использовать 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();
        }

    }
}
1 голос
/ 27 июня 2010

Да, я думаю, что это возможно. Вам просто нужно сохранить где-то крайний срок для задания, а затем, используя 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();
    }
}

С этим доказательством концепции вы могли бы использовать очередь и выставить исполняемые объекты во время их выполнения и вернуть их обратно после завершения выполнения вместо использования простого списка.

Кроме того, могут быть некоторые проблемы с синхронизацией, но с предоставленной вами информацией, я думаю, этого будет достаточно.

Надеюсь, это поможет.

...