• 12 hours
  • Hard

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 11/8/22

Use Industry-Standard Concurrency Safeguards

Evaluated skills

  • Use industry-standard concurrency safeguards

Description

This quiz will evaluate your understanding of how to build thread-safe applications that need coordination across threads. You'll create tasks that cooperate using ForkJoinTasks and CompletableFutures while applying your knowledge of Java's synchronizers, including RenentrantLocks, Semaphores, CountdownLatches.

We'll write code that uses concurrency to manipulate a list of stars for various ends. Let's start with a star entity that is composed of the star's name and the number of Solar Masses representing its size.

public class Star {
// For prototyping. Don't break encapsulation in real code.
public String name;
public Double solarMasses;
public Star(String name, Double solarMasses) {
this.name = name;
this.solarMasses = solarMasses;
}
}

We also have a factory class for creating a list with a sample set of 2000 stars.

public class FakeStarListFactory {
// make a list of 2000 fake stars
public static List<Star> make() {
List<Star> stars = IntStream.range(0,2000).
// Create "planet:n" with a random earth mass
mapToObj( n -> new Star("Star-" + n, Math.random() ) ).
// Create a list with a mutable reduction
collect(Collectors.toList());
return stars;
}
}
  • Question 1

    You are called in by a crack team of budget astronomers who want to take advantage of a special offer and get some free telescopes. They need to concurrently send the names of stars as discount codes to an online shop, which is giving away free telescopes.

    To keep things fair, the store only allows two purchases at a time from any customer. You've been asked to modify some code to use the following class's  buy(discountCode, item)  method to purchase a telescope item. 

    public class AstronomyShopClient {
    public void buy(String discountCoude, String item) {
    System.out.println("Ordered a telescope");
    }
    }

    To order a telescope, someone needs to call the method with a star's name and a telescope argument. Eg.,  client.buy(star.name, "telescope") 

    They tried to implement a solution that would split the list of planets in two and kick-off two asynchronous CompletableFuture, which loop through their respective lists and order telescopes. It hasn't worked. You've been told that it only seems to be looping through one list at a time.

    You are given a FreeTelescopeGrabber class which looks like this:

    public class FreeTelescopeGrabber {
    private static final String ITEM = "telescope";
    private AstronomyShopClient client = new AstronomyShopClient();
    private static final List<Star> STAR_LIST = FakeStarListFactory.make();
    public CompletableFuture orderTelescopes() {
    // Split our list in 2
    List<Star> leftList = stars.subList(
    0, stars.size()/2);
    List<Star> rightList = stars.subList(
    stars.size()/2, stars.size());
    // Submit orders for both sides of the list
    return CompletableFuture.allOf(
    CompletableFuture.runAsync(
    ()->orderWithDiscountCodes(leftList)
    ).thenRun(
    ()->orderWithDiscountCodes(rightList)
    )
    );
    }
    private void orderWithDiscountCodes(List<Star> starts) {
    // Use the client to order a free telescope
    // using each star as a discount code
    for (int index =0; i<stars.size(); i++) {
    client.get(star.name, ITEM);
    }
    }
    }

    Which of the following changes would fix this? 

    Careful, there are several correct answers.
    • // Submit orders for both sides of the list
      return CompletableFuture.anyOf(
      CompletableFuture.runAsync(
      ()->orderWithDiscountCodes(leftList)
      ),
      CompletableFuture.runAsync(
      ()->orderWithDiscountCodes(rightList)
      )
      );

       

    • return CompletableFuture.
      runAsync( ()->orderWithDiscountCodes(leftList) ).
      thenRunAsync( ()->orderWithDiscountCodes(rightList) );

       

    • // Submit orders for both sides of the list
      return CompletableFuture.allOf(
      CompletableFuture.runAsync(
      ()->orderWithDiscountCodes(leftList)
      ),
      CompletableFuture.runAsync(
      ()->orderWithDiscountCodes(rightList)
      )
      );

       

    • // Submit orders for both sides of the list
      return CompletableFuture.allOf(
      ()->orderWithDiscountCodes(leftList)
      ()->orderWithDiscountCodes(rightList)
      );
    • // Submit orders for both sides of the list
      CompletableFuture left =
      CompletableFuture.supplyAsync(()->leftList).
      thenAcceptAsync(
      (leftList)->orderWithDiscountCodes(leftList));
      CompletableFuture right =
      CompletableFuture.supplyAsync(()->rightList).
      thenAcceptAsync(
      (rightList)->orderWithDiscountCodes(rightList));
      return CompletableFuture.allOf( left, right );
  • Question 2

    The team of bargain-loving astronomers receives a notification from the store warning that you're making too many requests to their platform. While they welcome your business, for a short amount of time, they require you to restrict your application so that it only makes one request at a time on their service. Their longer-term plan is to gradually increase the number of concurrent orders that each customer can make.

    Given this information, which of the following changes would limit your application to ONE ORDER at a time AND require changing a single argument if you need to modify this limit?

    • public class FreeTelescopeGrabber {
      private static final Integer CONCURRENT_REQUEST_LIMIT = 1;
      private CountDownLatch requestLatch = new CountDownLatch(1);
      ...
      private void orderWithDiscountCodes(List<Star> starts) {
      for (int index =0; i<stars.size(); i++) {
      try {
      client.get(star.name, ITEM);
      } finally {
      requestLatch.countDown();
      }
      requestLatch.await();
      }
      }
      }

       

    • import java.util.concurrent.locks.ReentrantLock;
      public class FreeTelescopeGrabber {
      private static final ReentrantLock requestLock = new ReentrantLock();
      ...
      private void orderWithDiscountCodes(List<Star> starts) {
      for (int index =0; i<stars.size(); i++) {
      try {
      requestLock.lock();
      client.get(star.name, ITEM);
      } finally {
      requestLock.unlock();
      }
      }
      }
      }

       

    • public class FreeTelescopeGrabber {
      private static final Integer ORDER_LIMIT = 1;
      ...
      private void orderWithDiscountCodes(List<Star> starts) {
      for (int index =0; i<ORDER_LIMIT; i++) {
      client.get(star.name, ITEM);
      }
      }
      }
    • public class FreeTelescopeGrabber {
      private static final Integer CONCURRENT_REQUEST_LIMIT = 1;
      private Semaphore requestControlSemaphore =
      new Semaphore(CONCURRENT_REQUEST_LIMIT)
      ...
      private void orderWithDiscountCodes(List<Star> starts) {
      for (int index =0; i<stars.size(); i++) {
      requestControlSemaphore.acquire();
      client.get(star.name, ITEM);
      requestControlSemaphore.release();
      }
      }
      }

       

  • Question 3

     Someone on your team creates a TelescopeOrderService to order those free telescopes. It watches out for the IOExceptions, which sometimes happen when ordering a telescope. Here's the code. 

    public class TelescopeOrderService {
    // A client which will occaisonally fail
    public static class ShopClientSimulation {
    public static void orderTelescope(String discount)
    throws IOException {
    if (Math.random()>0.1) {
    throw new IOException("Simulated failure");
    }
    }
    }
    private Semaphore semaphore;
    public TelescopeOrderService(Semaphore semaphore) {
    this.semaphore = semaphore;
    }
    public void orderATelescope(String starName) {
    try {
    semaphore.acquire();
    ShopClientSimulation.orderTelescope(starName);
    semaphore.release();
    } catch (InterruptedException | IOException e) {
    e.printStackTrace();
    }
    }
    }

    The code gets stuck from time to time.

    You can try this out for yourself by starting JShell and pasting the code; although, you might have to call it several times. You can then create an instance and call it with different star names. Here's some example output:

    jshell> TelescopeOrderService tos = new TelescopeOrderService(new Semaphore(1));
    tos ==> TelescopeOrderService@73a8dfcc
    jshell> tos.orderATelescope("Sirius")
    jshell> tos.orderATelescope("Betelgeuse")
    jshell> tos.orderATelescope("Rigel")
    java.io.IOException: Simulated failure
    at REPL.$JShell$14$TelescopeOrderService$ShopClientSimulation.orderTelescope($JShell$14.java:13)
    jshell> tos.orderATelescope("Vega")
    ---- THIS IS WHERE THE CODE GETS STUCK - HIT CTRL-C to GET OUT ----

    How should you fix this?

    • Replace the semaphore with a ReentrantLock while also replacing  acquire()  and  release()  with  lock()  and  unlock().

    • Document that callers of  orderATelescope(String starName)  are responsible for calling  release()  on the semaphore they've passed to this class.

    • Remove the  catch  block and change the method declaration to  public void orderATelescope(String starName) throws InterruptedException, IOException {

    • Add a  finally  block after the  catch  block and call  semaphore.release()  within it.

Ever considered an OpenClassrooms diploma?
  • Up to 100% of your training program funded
  • Flexible start date
  • Career-focused projects
  • Individual mentoring
Find the training program and funding option that suits you best