停止整个生产者和消费者线程并将控制权交给主线程

时间:2016-08-28 22:01:07

标签: java multithreading

DefaultRunners是制片人 而OrderTaker是消费者

他们共享OrderQueue

目前,我使用变量isDone来表示游戏是否已完成。

每轮完成后,我想让它一次又一次地重复。

但是,在我目前的实施中,它只运行一次。

我怎么解决?

public class OrderQueue {

    public synchronized void pushOrder(Order order) throws InterruptedException {
        if (isDone) {
            wait();
        } else {
            runnersQueue.addLast(order);
            notifyAll();
        }
    }

    public void pullOrder() {
        try {
            if (runnersQueue.size() == 0) {
            } else if (isDone) {
                wait();
            } else {
                handleOrder(runnersQueue.pop());
            }
        } catch (InterruptedException e) {
    }

}

在我的主要班级

while(true){
    enterYesToStart();
    DefaultRunners dfltRunner = new DefaultRunners(queue);
    OrderTaker taker = new OrderTaker(queue);
    taker.run();


    System.out.println("This round is finished"); # never reach to this line
}

以下是示例

的完整源代码

https://gist.github.com/poc7667/d98e3bf5b3b470fcb51e00d9a0d80931

1 个答案:

答案 0 :(得分:0)

我已经看过你的代码片段,问题非常明显。 主线程运行OrderTaker runnable。主线程被卡在永久循环中,因为while语句无法完成,除非它抛出异常。 (请注意,ThreadRunner runnable也是如此。)

这意味着在比赛已经完成的情况下我还在拉动订单的主线程。

一旦比赛结束,OrderTaker应该在循环时退出。我想有多种方法可以实现这一点,但一种方法是使用共享变量。

我拿了你的代码并将其改编成一个有效的例子。

import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class RaceApp {

    public static void main(String[] args) throws InterruptedException {
        final RaceUpdateManager queue = new RaceUpdateManager();
        for (int i = 0; i < 3; i++) {
            queue.reset();
            List<Thread> threads = Arrays.asList(
                    new Thread(new Runner("Tortoise", 0, 10, queue)),
                    new Thread(new Runner("Hare", 90, 100, queue))
            );
            for (Thread thread : threads) {
                thread.start();
            }
            RaceUpdatesProcessor processor = new RaceUpdatesProcessor(queue);
            processor.run();
            System.out.println("Game finished");
        }
    }

    private static class RaceUpdateManager {
        private static final int TOTAL_DISTANCE = 300;

        //thread-safe implementation for queue so no external syncrhonization is required when adding/removing updates
        private final Deque<RaceUpdate> runnersQueue = new ConcurrentLinkedDeque<>();

        //lock used to sync changes to runnersRecords and done variables
        private final ReadWriteLock raceStatusLock = new ReentrantReadWriteLock();

        private final Map<String, Integer> runnersRecords = new HashMap<>();
        private volatile boolean raceDone = false;//volatile keyword guarantees visibility of changes to variables across threads

        public boolean isRaceDone() {
            return raceDone;
        }

        //updates can by added simultaneously (read lock)
        public void register(RaceUpdate raceUpdate) throws InterruptedException {
            Lock readLock = raceStatusLock.readLock();
            readLock.lock();
            try {
                if (!raceDone) {
                    runnersQueue.addLast(raceUpdate);
                }//ignore updates when the race is done
            } finally {
                readLock.unlock();
            }
        }

        //but they need to be processed in order (exclusive write lock)
        public void processOldestUpdate() {
            Lock writeLock = raceStatusLock.writeLock();
            writeLock.lock();
            try {
                RaceUpdate raceUpdate = runnersQueue.poll();
                if (raceUpdate != null) {
                    handleUpdate(raceUpdate);
                }
            } finally {
                writeLock.unlock();
            }
        }

        private void handleUpdate(RaceUpdate raceUpdate) {
            Integer distanceRun = runnersRecords.merge(
                    raceUpdate.runner, raceUpdate.distanceRunSinceLastUpdate, (total, increment) -> total + increment
            );

            System.out.printf("%s: %d\n", raceUpdate.runner, distanceRun);

            if (distanceRun >= TOTAL_DISTANCE) {
                raceDone = true;
                System.out.printf("Winner %s\n", raceUpdate.runner);
            }
        }

        public void reset() {
            Lock writeLock = raceStatusLock.writeLock();
            writeLock.lock();
            try {
                runnersQueue.clear();
                runnersRecords.clear();
                raceDone = false;
            } finally {
                writeLock.unlock();
            }
        }
    }

    public static class Runner implements Runnable {
        private final String name;
        private final int rest;
        private final int speed;
        private final RaceUpdateManager queue;
        private final Random rand = new Random();

        public Runner(String name, int rest, int speed, RaceUpdateManager queue) {
            this.name = name;
            this.rest = rest;
            this.speed = speed;
            this.queue = queue;
        }

        @Override
        public void run() {
            while (!queue.isRaceDone()) {
                try {
                    if (!takeRest()) {
                        queue.register(new RaceUpdate(this.name, this.speed));
                    }
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    //signal that thread was interrupted and exit method
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }

        private boolean takeRest() {
            return rand.nextInt(100) < rest;
        }
    }

    public static class RaceUpdatesProcessor implements Runnable {
        private final RaceUpdateManager queue;

        public RaceUpdatesProcessor(RaceUpdateManager queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            while (!queue.isRaceDone()) {
                try {
                    queue.processOldestUpdate();
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    //signal that thread was interrupted and exit method
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    }

    public static class RaceUpdate {
        public final String runner;
        public final int distanceRunSinceLastUpdate;

        public RaceUpdate(String runner, int distanceRunSinceLastUpdate) {
            this.runner = runner;
            this.distanceRunSinceLastUpdate = distanceRunSinceLastUpdate;
        }
    }
}