代码之家  ›  专栏  ›  技术社区  ›  JRL

How to make N runnables run for random periods of time, multiple times?

  •  3
  • JRL  · 技术社区  · 14 年前

    假设您有n个可运行的对象,并且对于每个对象,您希望它们随机执行一段时间。一旦一个可运行对象在该时间段内执行,您就需要重新安排它在另一个随机时间段内运行。您希望能够多次为每个Runnables执行此操作。

    一旦一个可运行文件被启动,它就应该在一个不确定的循环中执行一些事情——也就是说,可运行文件不应该知道它将运行多长时间。从runnable的角度来看,它将无限期运行。

    如何做到这一点,理想地只使用标准的Java API?如果这不可能实现原样,哪种替代设计最接近?

    3 回复  |  直到 14 年前
        1
  •  3
  •   Peter Lawrey    14 年前

    You might find this simpler.

    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
  •  2
  •   Kiril    14 年前

    毕竟…you have to use a TimerTask in combination with a Timer . I hope this is the final installment :)!

    You should try something like this:

    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();
            }
    
        }
    }
    
        3
  •  1
  •   OscarRyz    14 年前

    Yeap, I think it is possible. You just have to keep somewhere the deadline for the task and then using a Timer you can just check if the given TimerTask should keep running or the timer should be cancelled.

    Here's a complete example. It's far from perfect but just is the proof of concept on how should it work.

    对于可运行列表,您可以启动一个新的 ExecuteTask

    注意Runnabes没有意识到它们是否会永远运行。

    In the code below, I reschedule every second, and the random is within a 10 secs range, but you can reschedule every millisecond, and up to whatever time is reasonable for you.

    例如:

     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();
        }
    }
    

    With this proof of concept, you could use a queue and put out the runnables while they are executing and put them back when they have finished their execution, instead of using a simple list.

    Also, there might be some synchronization issues, but with the information you provided I think this would be enough.

    I hope it helps.