2010-06-26 79 views
3

假设您有N个可运行对象,并且您希望它们随机执行一段时间。一旦可运行对象在该时间段内执行,您想重新安排它运行另一个随机时间段。您希望能够多次为每个可运行对象执行此操作。如何让N个runnables随机运行多次,多次?

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

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

+2

它几乎听起来像你想写你自己的线程调度。 – 2010-06-26 21:31:53

+0

看看ScheduledExecutorService和CyclicBarrier/CountDownLatch。 – 2010-06-26 22:06:00

+0

当您引用runnable时,是否暗示多线程? – OscarRyz 2010-06-26 22:35:27

回答

2

您可能会发现这更简单。

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

毕竟..你必须使用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(); 
     } 

    } 
} 
+0

你能详细说明一下吗?假设你有两个可运行的对象R1和R2,你如何让R1运行2秒,然后是5,然后是1,然后是8等,然后是R2,然后是6,然后是2,然后是9,然后是3,然后推广对于随机时间和随机数的执行和随机数的可运行? – JRL 2010-06-26 21:51:44

+0

@JRL,我为你编了一些代码......不知道它是否编译,但它应该给你一个你如何实现你想要实现的东西的好主意。 – Kiril 2010-06-26 22:02:44

+0

@Lirik:谢谢,但是在你的场景中,你告诉可运行多长时间运行。理想情况下,我希望runnable完全不知道这一点,那是从runnable的角度来看,它运行无限期,并在未来的某个随机时间它会关闭,然后一些随机时间后,重新启动等。 – JRL 2010-06-26 22:10:07

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

有了这个概念证明的,你可以使用一个队列,并推出,而他们正在执行的可运行,并把他们回来时,他们已经完成,而不是使用一个简单的列表它们的执行。

此外,可能会有一些同步问题,但根据您提供的信息,我认为这已经足够了。

我希望它有帮助。