Let's say we have a service that buys some product. It needs to fetch the price and quantity of the product. Fetching the price usually takes longer, so we delegate this task to a thread, while we are dealing with quantity.
To keep things simple, our PriceChecker class will be just simulating that it does something meaningful:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PriceChecker { | |
private static final Random RANDOM = new Random(); | |
public Double checkPrice() { | |
int sleepTime = RANDOM.nextInt(3); | |
try { | |
Thread.sleep(sleepTime * 1000); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
return Math.random(); | |
} | |
} |
Now it would be good to somehow get the result of checkPrice() invocation. Runnable's run() is a void method, so we would have to do something like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Buyer { | |
private PriceChecker priceChecker = new PriceChecker(); | |
private Double price; | |
public void buyProduct() { | |
new Thread(new Runnable() { | |
@Override | |
public void run() { | |
price = priceChecker.checkPrice(); | |
} | |
}).start(); | |
int quantity = getQuantity(); | |
while (price == null) { | |
try { | |
Thread.sleep(10); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
System.out.println("Buying product, price = " + price + " , quantity = " + quantity); | |
} | |
private int getQuantity() { | |
return new Random().nextInt(100); | |
} | |
} |
This approach has a lot of drawbacks. We have to check in the loop if the price has already been set. What is more, price cannot be a final variable but has to be a field instead.
To deal with this kind of a problem, the Future interface should be used. Basically, it allows to get the result of thread's execution. Let's take a look at the actual usage in the context of our example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class BuyerWithFuture { | |
private PriceChecker priceChecker = new PriceChecker(); | |
private ExecutorService executorService = Executors.newSingleThreadExecutor(); | |
public void buyProduct() throws Exception { | |
Callable<Double> getPriceTask = new Callable<Double>() { | |
@Override | |
public Double call() throws Exception { | |
return priceChecker.checkPrice(); | |
} | |
}; | |
Future<Double> priceFuture = executorService.submit(getPriceTask); | |
Double price = priceFuture.get(); | |
int quantity = getQuantity(); | |
System.out.println("Buying product, price = " + price + " , quantity = " + quantity); | |
executorService.shutdown(); | |
} | |
private int getQuantity() { | |
return new Random().nextInt(100); | |
} | |
} |
The runner for both cases is pretty straightforward:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class FutureExampleRunner { | |
public static void main(String[] args) throws Exception { | |
Buyer buyerWithoutFuture = new Buyer(); | |
buyerWithoutFuture.buyProduct(); | |
BuyerWithFuture buyerWithFuture = new BuyerWithFuture(); | |
buyerWithFuture.buyProduct(); | |
} | |
} |
No comments:
Post a Comment