In multithreading: How to determine which thread stops first
Write a class named RaceHorse that extends Thread. Each RaceHorse has a name and run() method that displays the name 5000 times. Write a Java application that instantiates 2 RaceHorse objects. The last RaceHorse to finish is the loser.
This is the question. I have written the code for the two classes two run the thread Here are the codes:
RaceHorse
class RaceHorse extends Thread
{
public String name;
public RaceHorse(String name)
{
this.name = name;
}
public void run()
{
for(int i = 1 ; i <= 5000; i++)
{
System.out.println(i+" "+name);
}
System.out.println(name+" finis开发者_如何学Gohed.");
}
}
Runner
class Runner{
public static void main(String args[])
{
RaceHorse obj = new RaceHorse("Lol");
RaceHorse obj2 = new RaceHorse("BOL");
Thread t = new Thread(obj);
Thread t2 = new Thread(obj2);
t.start();
t2.start();
}
}
Now my problem is I am unable to find which of the thread finishes first and which seconds, i.e. which of the horse wins and which loses.!
First off: your RaceHorse
objects are themselves threads. You should be able to say obj.start();
and it'd work just as well. So remove t
and t2
entirely.
Next, you'll need some way to notify the main thread about the winner.
public void run()
{
... your loop stuff ...
// this is how we're going to do the notification.
Runner.done();
}
public class Runner
{
private static RaceHorse winner = null;
synchronized static void done()
{
// Threads calling this are going to be RaceHorse objects.
// Now, if there isn't already a winner, this RaceHorse is the winner.
if (winner == null) winner = (RaceHorse) Thread.currentThread();
}
public static void main(String[] args)
{
... create the horses ...
// start the horses running
obj.start();
obj2.start();
// wait for them to finish
obj.join();
obj2.join();
System.out.println(winner.name + " wins!");
}
}
There's no doubt a better way, but one method might be to create a class (e.g. 'Trophy') that is thread safe, has a method 'getTrohpy' that only returns true on the first call, and pass a reference to an instance of Trophy to both threads.
public class StackOverflow {
public static void main(String[] args) {
RaceHorse obj = new RaceHorse("Lol");
RaceHorse obj2 = new RaceHorse("BOL");
Thread t = new Thread(obj);
Thread t2 = new Thread(obj2);
t.start();
t2.start();
}
}
class RaceHorse extends Thread
{
//public String name;
public RaceHorse(String name)
{
this.setName(name);
}
public void run()
{
for(int i = 1 ; i <= 5000; i++)
{
System.out.println(i+" "+this.getName());
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.getName()+" finished.");
}
}
As cHao pointed out, RaceHorse
extends Thread
but you are creating a new Thread
per horse. I would solve it the opposite way, by having RaceHorse
implement Runnable
instead.
Secondly, the solution using a synchronized
method will work, but a general rule is always look for a class in java.util.concurrent
that will solve the problem first. This one can be solved using an AtomicReference
to ensure that only one horse takes the trophy.
Lastly, there could be a bias in favour of horse #1, if the main thread starts the horses' threads in a fixed order (this depends on the VM and on the overhead of starting a new thread on your OS.) Consider using a signal (for example a CountDownLatch
) that all horses wait for before starting.
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
public class Runner {
public static void main(String args[]) {
AtomicReference<RaceHorse> winner =
new AtomicReference<RaceHorse>();
CountDownLatch startingPistol = new CountDownLatch(1);
RaceHorse horse1 = new RaceHorse("Lol", startingPistol, winner);
RaceHorse horse2 = new RaceHorse("BOL", startingPistol, winner);
Thread thread1 = new Thread(horse1);
Thread thread2 = new Thread(horse2);
thread1.start();
thread2.start();
startingPistol.countDown();
}
}
class RaceHorse implements Runnable {
private final String name;
private final CountDownLatch startingPistol;
private final AtomicReference<RaceHorse> winner;
public RaceHorse(String name,
CountDownLatch startingPistol,
AtomicReference<RaceHorse> winner)
{
this.name = name;
this.startingPistol = startingPistol;
this.winner = winner;
}
public void run()
{
try {
startingPistol.await();
for(int i = 1 ; i <= 5000; i++)
{
System.out.println(i+" "+name);
}
boolean iWon = winner.compareAndSet(null, this);
System.out.printf("%s %s.%n", name, iWon? "won": "lost");
} catch (InterruptedException ex) {
System.out.printf("%s was assasinated before the race started.%n", name);
Thread.currentThread().interrupt();
}
}
}
I am not going to write the code for you; but you should take a look at the notify
method (see here) to be used.
One approach could be: once a thread has finished it will wait()
for the other thread(s) to notify (or notifyAll()
).
Another, more elegant solution, would consist of using a synchronized
block on a shared object; the syncrhonized(obj)
statement would be at the end of the run() method. Into that statement you could put a printline or any other code you would deem useful to determine who won the race.
This will work at the end of the main :
boolean alive1 = true;
boolean alive2 = true;
while (alive1 && alive2) {
alive1 = obj.isAlive();
alive2 = obj2.isAlive();
if (!alive1 && !alive2) {
// Too close to call
}
if (!alive1) {
// obj wins,
}
if (!alive2) {
// obj2 wins,
}
}
I'm late to the party, but I found this while looking for how to process the first result from a number of running threads. I think the easiest way is to use an ArrayBlockingQueue which gives you something like this.
public class RaceHorse extends Thread {
private ArrayBlockingQueue<RaceHorse> finishedRaceHorses;
public RaceHorse(String name) {
super(name);
}
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println(i + " " + getName());
}
System.out.println(getName() + " finished.");
finishedRaceHorses.offer(this);
}
public void setFinishedRaceHorses(ArrayBlockingQueue<RaceHorse> finishedRaceHorses) {
this.finishedRaceHorses = finishedRaceHorses;
}
}
public class Race {
private final List<RaceHorse> raceHorses;
public Race(List<RaceHorse> raceHorses) {
this.raceHorses = raceHorses;
}
public RaceHorse go() throws InterruptedException {
ArrayBlockingQueue<RaceHorse> finishedRaceHorses = new ArrayBlockingQueue<RaceHorse>(raceHorses.size());
for (RaceHorse raceHorse : raceHorses) {
raceHorse.setFinishedRaceHorses(finishedRaceHorses);
raceHorse.start();
}
return finishedRaceHorses.take();
}
}
public class Runner {
public static void main(String args[])
{
RaceHorse horseOne = new RaceHorse("Lol");
RaceHorse horseTwo = new RaceHorse("BOL");
Race race = new Race(Arrays.asList(horseOne, horseTwo));
try {
RaceHorse winner = race.go();
System.out.println("The winner is " + winner.getName());
} catch (InterruptedException e) {
System.out.println("The race was interrupted, maybe by a streaker?");
}
}
}
I have tried this problem and solved it using following code. There is room for improvement but for me this code worked perfectly :
1.RacingGame.java
/
package game;
import gamingObject.Horse;
import gamingObject.Race;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RacingGame {
/**
* @param args
*/
public static Map<Integer, List<String>> raceToWinners = new HashMap<Integer, List<String>>();
public static int currentRace = 1;
public static boolean trackComplete = false;
private static boolean newTrackBegin;
private static boolean flag = true;
private static boolean race6Begin = false;
private static boolean race7Begin = false;
private static Object mutex = new Object();
private int frstHorseInNextRace = 0;
public static void main(String[] args) throws InterruptedException {
ExecutorService exeService = Executors.newFixedThreadPool(5);
/*
* Logic to conduct first 5 races (total horses/total track) so here
* total horses = 25 and tracks = 5 hence initial and compolsuary races
*/
RacingGame rg = new RacingGame();
for (int race = 1; race <= 5; race++) {
trackComplete = false;
currentRace = race;
while (!trackComplete) {
rg.startTrack();
}
}
/*
* Before 6th Race lets have right candidate for 6th race
*/
List<String> horseNames = chooseHorsesForRace6();
/*
* Race among 5 tops horses from 5 races
*/
currentRace++;
synchronized (mutex) {
while (!race6Begin) {
race(horseNames);
}
}
/*
* Choose candidates for last race 7
*/
horseNames = chooseHorsesForRace7();
currentRace++;
synchronized (mutex) {
while (!race7Begin) {
race(horseNames);
}
}
printResults();
System.exit(0);
}
private static void printResults() {
// TODO Auto-generated method stub
Iterator<Integer> iter = raceToWinners.keySet().iterator();
while (iter.hasNext()) {
int raceNum = iter.next();
StringBuffer sb = new StringBuffer();
System.out.println("Race" + raceNum + " : ");
List<String> horses = raceToWinners.get(raceNum);
for (int i = 0; i < 3; i++) {
sb.append(horses.get(i));
if (i < 2)
sb.append(",");
}
System.out.print(sb.toString());
System.out.println();
}
}
private static List<String> chooseHorsesForRace7() {
/*
* Adding First horse at first rank among 25 horses
*/
List<String> winners = new ArrayList<String>();
winners.add(raceToWinners.get(6).get(0));
raceToWinners.put(7, winners);
/*
* Taking first horses from races 2 and 3
*/
List<String> finalTrackHorses = new ArrayList<String>();
finalTrackHorses.add(raceToWinners.get(6).get(1));// firstHorse
finalTrackHorses.add(raceToWinners.get(6).get(2));// secondHorse
/*
* Rejecting all horses from race track whose first horses are at 4th
* and 5th rank of race 6
*/
for (int i = 1; i <= 5; i++) {
if (raceToWinners.get(i).contains(winners.get(0))) {
finalTrackHorses.add(raceToWinners.get(i).get(1));// thirdHorse
finalTrackHorses.add(raceToWinners.get(i).get(2));// forth horse
} else if (raceToWinners.get(i).contains(finalTrackHorses.get(1))) {
finalTrackHorses.add(raceToWinners.get(i).get(1));// fifth horse
}
}
return finalTrackHorses;
}
private static void race(List<String> horseNames) throws InterruptedException {
if (currentRace == 6)
race6Begin = true;
else
race7Begin = true;
newTrackBegin = true;
flag = true;
trackComplete = false;
while (flag) {
if (!trackComplete) {
/*
* Create thread for each horse
*
* Here taking slot of 5 horses and keep them running in a
* single loop.
*/
if (newTrackBegin) {
List<String> horses = Arrays.asList(horseNames.get(0),
horseNames.get(1), horseNames.get(2),
horseNames.get(3), horseNames.get(4));
Race r = new Race(horses);
r.start();
}
newTrackBegin = false;
mutex.wait(1);
} else if (trackComplete) {
mutex.notify();
flag = false;
}
}
}
private static List<String> chooseHorsesForRace6() {
List<String> lstHorses = new ArrayList<String>();
for (int i = 1; i <= 5; i++) {
/*
* Take only 1st Position Holders of first 5 races
*/
lstHorses.add(raceToWinners.get(i).get(0));
}
return lstHorses;
}
public Map<Integer, List<String>> getRaceToWinners() {
return raceToWinners;
}
public static synchronized void addTrackWinnerInList(String horseName) {
List<String> horses = raceToWinners.get(currentRace);
if (horses == null) {
List<String> raceHorses = new ArrayList<String>();
raceHorses.add(horseName);
raceToWinners.put(currentRace, raceHorses);
} else {
horses.add(horseName);
raceToWinners.put(currentRace, horses);
}
if (raceToWinners.get(currentRace) != null
&& raceToWinners.get(currentRace).size() == 5) {
trackComplete = true;
}
}
public static boolean isTrackComplete(){
return trackComplete;
}
public void startTrack() throws InterruptedException {
// TODO Auto-generated method stub
synchronized (mutex) {
flag = true;
newTrackBegin = true;
trackComplete = false;
while (!trackComplete) {
/*
* Create thread for each horse
*
* Here taking slot of 5 horses and keep them running in a
* single loop.
*/
if (newTrackBegin) {
List<String> horses = Arrays.asList("Horse"
+ (++frstHorseInNextRace), "Horse"
+ (++frstHorseInNextRace), "Horse"
+ (++frstHorseInNextRace), "Horse"
+ (++frstHorseInNextRace), "Horse"
+ (++frstHorseInNextRace));
Race r = new Race(horses);
r.start();
}
newTrackBegin = false;
}
}
}
}
2.Horse.java
package gamingObject;
import game.RacingGame;
public class Horse extends Thread{
String horseName;
public Horse(String horseName){
this.horseName = horseName;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
RacingGame.addTrackWinnerInList(this.horseName);
}
}
3.Race.java
package gamingObject;
import game.RacingGame;
import java.util.List;
public class Race extends Thread {
List<String> horses;
private boolean flag = true;
private Object obj = new Object();
public Race(List<String> horses) {
this.horses = horses;
}
public void startRace() {
synchronized (obj) {
run();
}
}
@Override
public void run() {
synchronized (obj) {
boolean newTrackBegin = true;
while (!RacingGame.isTrackComplete()) {
/*
* Create thread for each horse
*
* Here taking slot of 5 horses and keep them running in a
* single loop.
*/
if (newTrackBegin) {
Horse h1 = new Horse(horses.get(0));
Horse h2 = new Horse(horses.get(1));
Horse h3 = new Horse(horses.get(2));
Horse h4 = new Horse(horses.get(3));
Horse h5 = new Horse(horses.get(4));
Thread t1 = new Thread(h1);
Thread t2 = new Thread(h2);
Thread t3 = new Thread(h3);
Thread t4 = new Thread(h4);
Thread t5 = new Thread(h5);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
newTrackBegin = false;
}else{
if(!RacingGame.isTrackComplete()){
try {
obj.wait(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
obj.notify();
}
}
}
}
}
}
精彩评论