Worksheet: J6
Worksheets are self-guided activities that reinforce lectures. They are not graded for accuracy, only for completion. They are due, on github by 11:59pm on the day of the lecture.
Submit a file called worksheet-J6.md
in your repo for this assignment. Submit a file called worksheet-J6.md
in your repo for this assignment.
Note
Attempt to answer these questions before running the code. This will improve your ability to analyize and reason about code without an IDE or compiler. This skill we be helpful on the exams.
Questions
-
Compile the two files below and run the main method in the
SimpleThreadExample
.$ javac *.java $ java SimpleThreadExample
/* SimpleThreadExample.java */ public class SimpleThreadExample { public static void main(String[] args) { Messages msg = new Messages(); Thread morningThread = new GreetingsThread(msg, 0); Thread afternoonThread = new GreetingsThread(msg, 1); morningThread.start(); afternoonThread.start(); //MARK } }
/* Messages.java */ public class Messages { public String morning; public String afternoon; public String evening; public String night; public Messages() { morning = "Good morning!"; afternoon = "Good afternnon!"; evening = "Good evening!"; night = "Good night!"; } public String getMessage(int choice) { switch(choice) { case 0: return this.morning; case 1: return this.afternoon; case 3: return this.evening; case 4: return this.night; default: return ""; } } }
/* GreetingsThread.java */ public class GreetingsThread extends Thread { private Messages msg; private int choice; public GreetingsThread(Messages msg, int choice) { this.msg = msg; this.choice = choice; } public void run() { System.out.println("Started the " + msg.getMessage(choice) + " thread..."); //print 10 times for (int i = 0; i < 10; i++) { System.out.println(msg.getMessage(choice)); } System.out.println("Exiting the " + msg.getMessage(choice) + " thread..."); } }
Then:
- Run the program a few times. Describe the output of this program. Is it consistent?
- Draw a memory diagram of the program at
MARK
-
Now let’s modify
GreetingsThread
to add aThread.sleep()
in therun()
method. Recompile theGreatingThread
class as below.Run the program multiple times. Does the output change in any way? Does one thread always finish first, or does the order change?
/* GreetingsThread.java */ public class GreetingsThread extends Thread { private Messages msg; private int choice; public GreetingsThread(Messages msg, int choice) { this.msg = msg; this.choice = choice; } public void run() { System.out.println("Started the " + msg.getMessage(choice) + " thread..."); for (int i = 0; i < 10; i++) { System.out.println(msg.getMessage(choice)); try { // Sleep for 1 second (1000 milliseconds) Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Interrupted while sleeping..."); } } System.out.println("Exiting the " + msg.getMessage(choice) + " thread..."); } }
-
Now let’s add a
System.out.println()
at the end of the main method. Recompile the program with this addition, continuing from above. Explain how it is possible that the main method is complete but the program is still producing output./* SimpleThreadExample.java */ public class SimpleThreadExample { public static void main(String[] args) { Messages msg = new Messages(); Thread morningThread = new GreetingsThread(msg, 0); Thread afternoonThread = new GreetingsThread(msg, 1); morningThread.start(); afternoonThread.start(); System.out.println("Main method exiting..."); } }
-
Finally let’s add a
thread.join()
to join themorningThread
for 5 seconds before starting theafternoonThread
. Recompile and rerun. Then describe the output of this program. Explain how attempting to join the first thread for 5 seconds affects the output of this program./* SimpleThreadExample.java */ public class SimpleThreadExample { public static void main(String[] args) { Messages msg = new Messages(); Thread morningThread = new GreetingsThread(msg, 0); Thread afternoonThread = new GreetingsThread(msg, 1); morningThread.start(); try { System.out.println("Joining the morning thread for 5 seconds..."); morningThread.join(5000); } catch (InterruptedException e) { System.out.println("Interrupted while joining a thread..."); } afternoonThread.start(); System.out.println("Main method exiting..."); } }
-
What does
join()
do compared tojoin(10)
? -
What is the difference between
isAlive()
andjoin()
? (note, no arguments tojoin
.) -
Compile the two files below and run the main method in the
AnimalFootRace
. Then describe the output of this program. Explain why the threads finish in the order that they do.$ javac *.java $ java AnimalFootRace
/* AnimalFootRace.java */ public class AnimalFootRace { public static void main(String[] args) { Thread tortoiseThread = new AnimalRacerThread("Tortoise", 5); Thread hareThread = new AnimalRacerThread("Hare", 20); Thread cheetahThread = new AnimalRacerThread("Cheetah", 50); System.out.println("On your marks, get set, go!"); tortoiseThread.start(); hareThread.start(); cheetahThread.start(); } }
/* AnimalRacerThread.java */ public class AnimalRacerThread extends Thread { public static final int NUM_LAPS = 8; private String animalName; private int animalSpeed; public AnimalRacerThread(String animalName, int animalSpeed) { this.animalName = animalName; this.animalSpeed = animalSpeed; } @Override public void run() { for (int i = 1; i <= NUM_LAPS; i++) { System.out.println(animalName + " lap " + i); try { Thread.sleep(1000 / animalSpeed); } catch (InterruptedException e) { System.out.println("Interrupted while sleeping..."); } } System.out.println(animalName + "Finished!"); } }
-
Modify the
AnimialFootRace
main method. Add additional code to give the hare a head start, without modifying the animal speeds. Then compile the two files below and run the main method in theAnimalFootRace
. Provide the modified code below. Then describe the output of this program. Explain why your modification worked or did not work.$ javac *.java $ java AnimalFootRace
/* AnimalFootRace.java */ public class AnimalFootRace { public static void main(String[] args) { Thread tortoiseThread = new AnimalRacerThread("Tortoise", 5); Thread hareThread = new AnimalRacerThread("Hare", 20); Thread cheetahThread = new AnimalRacerThread("Cheetah", 50); System.out.println("On your marks, get set, go!"); tortoiseThread.start(); hareThread.start(); cheetahThread.start(); } }
/* AnimalRacerThread.java */ public class AnimalRacerThread extends Thread { public static final int NUM_LAPS = 8; private String animalName; private int animalSpeed; public AnimalRacerThread(String animalName, int animalSpeed) { this.animalName = animalName; this.animalSpeed = animalSpeed; } @Override public void run() { for (int i = 1; i <= NUM_LAPS; i++) { System.out.println(animalName + " lap " + i); try { Thread.sleep(1000 / animalSpeed); } catch (InterruptedException e) { System.out.println("Interrupted while sleeping..."); } } System.out.println(animalName + "Finished!"); } }
-
Modify the
AnimialFootRace
main method. Add a new animal thread to the foot race. Then compile the two files below and run the main method in theAnimalFootRace
. Provide the modified code below. Then describe the output of this program.$ javac *.java $ java AnimalFootRace
/* AnimalFootRace.java */ public class AnimalFootRace { public static void main(String[] args) { Thread tortoiseThread = new AnimalRacerThread("Tortoise", 5); Thread hareThread = new AnimalRacerThread("Hare", 20); Thread cheetahThread = new AnimalRacerThread("Cheetah", 50); System.out.println("On your marks, get set, go!"); tortoiseThread.start(); hareThread.start(); cheetahThread.start(); } }
/* AnimalRacerThread.java */ public class AnimalRacerThread extends Thread { public static final int NUM_LAPS = 8; private String animalName; private int animalSpeed; public AnimalRacerThread(String animalName, int animalSpeed) { this.animalName = animalName; this.animalSpeed = animalSpeed; } @Override public void run() { for (int i = 1; i <= NUM_LAPS; i++) { System.out.println(animalName + " lap " + i); try { Thread.sleep(1000 / animalSpeed); } catch (InterruptedException e) { System.out.println("Interrupted while sleeping..."); } } System.out.println(animalName + "Finished!"); } }
-
The
Singleton
class below implements theSingleton
pattern. The singleton pattern is a software engineering design pattern that restricts the instantiating of a class to a single instance. Review theSingleton
class code below. Is it possible to create more than one instance of theSingleton
class if two threads attempt to call thegetInstance()
method at the same time?// Java program to create a Singleton class. public class Singleton { // This is a private member variable so that the singletonInstance // can only be accessed through the getInstance() method. private static Singleton singletonInstance; // Private constructor forces the class to be instantiated // via the getInstance method. private Singleton() { // private constructor. } // Method to get an instance of this class. public static Singleton getInstance() { // If this singleton instance is null, // then construct a new instance. // Otherwise return the existing instance. if (singletonInstance == null) { singletonInstance = new Singleton(); } return singletonInstance; } }
-
Review the modified thread safe Singleton class code below. Is it possible to create more than one instance of the
Singleton
class if two threads attempt to call thegetInstance()
method at the same time? How does the synchronized keyword affect the attempts to call thegetInstance
method from multiple threads at the same time?// Java program to create thread safe Singleton class. public class Singleton { // This is a private member variable so that the singletonInstance // can only be accessed through the getInstance() method. private static Singleton singletonInstance; // Private constructor forces the class to be instantiated // via the getInstance method. private Singleton() { // private constructor. } // Synchronized method to control simultaneous access // to the getInstance method. synchronized public static Singleton getInstance() { // If this singleton instance is null, // then construct a new instance. // Otherwise return the existing instance. if (singletonInstance == null) { singletonInstance = new Singleton(); } return singletonInstance; } }
-
Review the
ThreadRunner
class code below. Compile and run theThreadRunner
main method. Describe the output. Does the execution halt? DoesSimpleThreadTwo
finish running? If not, why doesSimpleThreadTwo
get stuck in the while loop (hint: recall the compiler optimization example)?$ javac ThreadRunner.java $ java ThreadRunner
/* ThreadRunner.java */ public class ThreadRunner { private static boolean statusFlag = false; private static class SimpleThreadOne extends Thread { public void run() { for (int i = 1; i <= 2000; i++){ System.out.println("Simple thread one counter - " + i); } // Change the status flag. statusFlag = true; System.out.println("Status flag changed to true in simple thread one."); } } private static class SimpleThreadTwo extends Thread { public void run() { int waitCounter = 1; while (!statusFlag) { waitCounter++; } System.out.println("Start simple thread two processing " + waitCounter); } } public static void main(String[] args) { SimpleThreadOne simpleThreadOne = new SimpleThreadOne(); simpleThreadOne.start(); SimpleThreadTwo simpleThreadTwo = new SimpleThreadTwo(); simpleThreadTwo.start(); } }
-
Review the modified
ThreadRunner
class code below. Compile and run theThreadRunner
main method. Describe the output. Does the execution halt? DoesSimpleThreadTwo
finish running? If it does finish running, why does adding the volatile keyword before the booleanstatusFlag
change the behavior of the code compared to the previous question?$ javac ThreadRunner.java $ java ThreadRunner
/* ThreadRunner.java */ public class ThreadRunner { private static volatile boolean statusFlag = false; private static class SimpleThreadOne extends Thread { public void run() { for (int i = 1; i <= 2000; i++){ System.out.println("Simple thread one counter - " + i); } // Change the status flag. statusFlag = true; System.out.println("Status flag changed to true in simple thread one."); } } private static class SimpleThreadTwo extends Thread { public void run() { int waitCounter = 1; while (!statusFlag) { waitCounter++; } System.out.println("Start simple thread two processing " + waitCounter); } } public static void main(String[] args) { SimpleThreadOne simpleThreadOne = new SimpleThreadOne(); simpleThreadOne.start(); SimpleThreadTwo simpleThreadTwo = new SimpleThreadTwo(); simpleThreadTwo.start(); } }
-
A second price auction is an auction where the highest bidder only has to pay whatever the second highest bid was. For example if person A bids $1 and person B bids $2, then person B wins the auction, but only pays $1. Below are two classes that reflect this,
SecondPriceAuction
andBidder
.Review the code below. What might happen if two threads call the
makeBid
method at the same time? How would you modify the code to protect the auction outcome?public class SecondPriceAuction { private int currentHighestBid = 0; private int secondHighestBid = 0; public int getSecondHighestBid() { return secondHighestBid; } public void makeBid(int amount) { if (amount > currentHighestBid) { secondHighestBid = currentHighestBid; currentHighestBid = amount; } else if(amount > secondHighestBid) { secondHighestBid = amount; } } }
import java.util.Random; public class Bidder extends Thread { private SecondPriceAuction secondPriceAuction; private int maxBid; public Bidder(SecondPriceAuction secondPriceAuction, int maxBet) { this.secondPriceAuction = secondPriceAuction; this.maxBid = maxBet; } public void run() { try { Thread.sleep((new Random()).nextInt(100)); } catch (InterruptedException e) { System.out.println("Interrupted while sleeping..."); } secondPriceAuction.makeBid(maxBid); } }
public class AuctionRunner { public static void main(final String args[]) { SecondPriceAuction secondPriceAuction = new SecondPriceAuction(); Bidder[] bidders = new Bidder[3]; // Create new bidders. for (int i = 0; i < bidders.length; i++) { bidders[i] = new Bidder(secondPriceAuction, i + 1); } // Start bidding. for (int i = 0; i < bidders.length; i++) { bidders[i].start(); } // Ensures all threads have finished before we print out the price for (Bidder bidder : bidders) { try { bidder.join(); } catch (InterruptedException e) { System.out.println("Interrupted while joining a thread..."); } } System.out.println("Final Price: $" + secondPriceAuction.getSecondHighestBid()); } }