如何暂停Runnable直到另一个相同类型的任务完成?

时间:2017-02-22 03:23:52

标签: java multithreading runnable

我想创建一个程序来说明带有检查点的马拉松选手。创建了5个跑步者并且花费了一段随机的时间来到达第一个检查点。跑步者必须在检查站停下,直到所有其他跑步者到达检查站。有2个检查站和终点线。

以下是一些基本代码,我认为这是实现此目的的最佳方式:

public class Main {
public static void main(String[] args) {
    int i = 1;
    Runner[] runners = new Runner[5];

    //Create 5 Runners
    for (Runner runner : runners) {
        runner = new Runner(i++);
        runner.run();
    }

    //Wait for runners to arrive at 1st checkpoint
    for (Runner runner : runners) {
        runner.arrivedAt1stCheckpoint();
    }

    //Wait for runenrs to arrive at 2nd checkpoint
    for (Runner runner : runners) {
        runner.arrivedAt2ndCheckpoint();
    }

    //Wait for runners to finish race
    for (Runner runner : runners) {
        runner.finishedRace();
    }
}}

public class Runner implements Runnable {

    public final int runnerID;
    Random randomGenerator = new Random();

    public Runner(int i) {
        this.runnerID = i;
    }

    @Override
    public void run() {
        System.out.printf("Runner %d exists\n", runnerID);
    }

    public boolean arrivedAt1stCheckpoint() {
        sleepThread();
        System.out.printf("Runner %d arrived at 1st checkpoint\n", runnerID);
        return true;
    }

    public boolean arrivedAt2ndCheckpoint() {
        System.out.printf("Runner %d arrived at 2nd checkpoint\n", runnerID);
        sleepThread();
        return true;
    }

    public boolean finishedRace() {
        System.out.printf("Runner %d finished race\n", runnerID);
        sleepThread();
        return true;
    }

    private void sleepThread() {
        try {
            Thread.sleep(randomGenerator.nextInt(1000));
        } catch (InterruptedException ex) {
            Logger.getLogger(Runner.class.getName()).log(Level.SEVERE, null, ex);
        }
    }}

显然这只是结构,我缺少一堆东西,所以我不是要求为我编写整个程序。 我在这里遇到的主要问题是让Runner类的每个实例以某种方式与其他实例进行通信,并在整个程序中睡眠,直到所有Runners到达检查点。 任何“指针”都会有所帮助,谢谢!

2 个答案:

答案 0 :(得分:1)

你想要一种能够检查所有跑步者何时进入检查站的方法。这可以通过使用一些布尔变量来跟踪你的Runner是否已经使它成为checkpoint1,checkpoint2等。

public class Runner implements Runnable {    
    private boolean atFirstCheckpoint = false;
    // ... More checkpoint booleans 
    // ... Rest of the class

    public boolean hasReachedFirstCheckpoint() {
        return atFirstCheckpoint;
    }
}

如果我们保留一组跑步者(Array,ArrayList等),我们就可以创建一个方法来检查我们所有的跑步者是否都已到达检查点。

public boolean everyoneHasReachedFirstCheckpoint(Runner[] runners) {
    for (Runner r : runners) {
        if (!r.hasReachedFirstCheckpoint()) {
            return false;
        }
        return true;
    }
}

然后我们可以更改gotAt1stCheckpoint()来实现此检查。 Runner对象将休眠,直到阵列中的所有其他运行程序也到达检查点。

public boolean arrivedAt1stCheckpoint(Runner[] runners) {
    this.atFirstCheckPoint = true;
    System.out.printf("Runner %d arrived at 1st checkpoint\n", runnerID);
    while (!everyoneHasReachedFirstCheckpoint(runners)) {
        sleepThread();
    }

    return true;
}

编辑:值得注意的是,在执行while循环期间,整个运行程序线程将暂停X时间,具体取决于您的sleepThread()方法。这意味着跑步者在检查每个人是否已经进入检查点之前,会在X时间内睡着,这很可能会导致其他线程开始先行。

编辑:要尝试管理您的跑步者(线程),您可能会发现使用Executor界面和/或线程池非常有用。

答案 1 :(得分:1)

您可能需要考虑CyclicBarrier或CountDownLatch。这是一个使用CyclicBarrier的快速代码段。

将屏障设置为您想要的所需数量,将屏障传递给您想要在屏障上等待的线程,如下所示

int BarrierCount = 4;
CyclicBarrier barrier = new CyclicBarrier(BarrierCount);

for (int i = 0; i <= BarrierCount; i++) {
    (new AppThreadsForBarrier(barrier, "name"+i, ...)).start();
}

现在在需要在屏障上等待的线程中,可以执行以下操作

class AppThreadsForBarrier extends Thread {

    // barrier along with other variables you need for your thread
    public AppThreadsForBarrier(CyclicBarrier barrier, String name, ....) {
    ..

    public void run() {
        try {
            Thread.sleep(duration);
            System.out.println(Thread.currentThread().getName() + " is calling await()");
            barrier.await();
            System.out.println(Thread.currentThread().getName() + " has started running again");
            ..

关于在哪里使用CyclicBarrier v / s CountdownLatch存在一些细微差别,但这应该可以让你开始拍照。