Search code examples
javamultithreadingmultiprocessinghelperpi

pi estimation by throwing darts using multithreaded in java


I have to write a code to use multithreading for piEstimator. It is a simple algorithm that makes an estimate of Pi. The algorithm works by throwing theoretical darts at a dartboard darts that hit the board fall within the quarter circle darts that miss is outside it. The dartboard has a height and width of 1. We throw a dart by generating a random x and y coordinate for the dart between 0 and 1. I have calculated this using Pythagoras (x2+y2= dist2)

my code

import java.util.Random;

public class PiEstimator {
//number of iterations the algorithm, a larger number here should result in a more accurate estimate
public static int numberOfDarts = 100_000_000;
public static void main(String[] args) {
    //how many darts lie within the quarter circle region
    int within = 0;
    Random r = new Random();
    for(int i = 0; i< numberOfDarts; i++){
        //get x and y coordinates for the darts
        double x = r.nextDouble();
        double y = r.nextDouble();
        //calculate the distance from the origin (0, 0) darts with a distance less than 1 are within the
        //quarter circle so add these to within
        double dist = Math.sqrt((x*x) + (y*y));
        if(dist < 1)
            within++;
    }

    //estimate pi by getting proportion of darts in the quarter circle and multiplying by 4.
    double estimate = (double)within/numberOfDarts *4;
    System.out.println(estimate);

}
}

What I want to do is

  1. A TotalWithin class that keeps track of the number of darts that are within the dart board• A class that extends Thread which throws darts, works out their distance from the origin and increases the value held within TotalWithin if needed.

Solution

  • Every time 2 separate threads need to synchronize anything, you get a slowdown. Hence, trying to keep one counter that counts how many darts were thrown by all the threads is quite expensive, and doesn't seem relevant: It is much less tricky to gather up individual thread's total throw counts when they are done, or if you intent is to run them endlessly, for them to update the 'main counter' only every 1000 throws or so.

    The primitives you need to perform this task:

    Threads

    You start with encapsulating the 'job' of a dart thrower. Whatever state you want to look at is probably best stored as actual state - as fields of instances of such a class. You're going to make a bunch of these, and have one thread running each.

    public class DartThrower implements Runnable {
      private int within;
      private int thrown;
      
      @Override public void run() {
        Random r = new Random();
        for (int i = 0; i < MAX_THROW; i++) {
          // the usual logic goes here
        }
      }
    }
    

    Then to actually make a thread that runs a 'thrower' on its own, simply:

    DartThrower thrower = new DartThrower();
    new Thread(thrower).start();
    

    synchronization

    Anytime a thread accesses any field in any class anywhere, the JVM flips an evil coin. If the coin lands heads, you get a local cached copy of that field (unique to the thread - one thread reads '5', another may read '8', because they each have a local copy). On tails, the thread syncs up the cached copies with other threads.

    It's 'evil' in the sense that it may flip heads every time, reliably, throughout all your testing and your first week on production, and then it starts flipping tails juuust when that important prospective customer shows up and you're giving a demo. The name of the game is to prevent the JVM from flipping it. You do this by establishing happens-before relationships, which is quite detailed - usually you want ALL inter-thread communication to be tightly controlled, because you can't test this stuff.

    Useful synchronization primitives

    • In this case, AtomicInteger is great. You can use it to have a single counter that all the dart instances increment. That class promises syncing (but, all syncing is somewhat slow, so keep that in mind).

    • To speed things up, you can have your dart throwers update the 2 AtomicIntegers representing thrown and within for all throwers, only every 1000 throws or so, this should massively speed things up considerably.

    • Alternatively, don't have any global AtomicIntegers at all; just run your dart throwers and wait for them to complete the job. "The thread is now done" is guaranteed to happens-before any attempt to read anything that the now-done thread changed by any code that already established that the other thread is done. Thread itself isn't the right abstraction for this - for that you need an ExecutorPool.

    ExecutorPool

    This concept of "I have a task I want to do a few million times, each 'run' is independent of all others, and I just want to merge some 'result' values from each runner once they are all done, then all I need is the merged results" - is very common. ExecutorPool is a class baked into core java that helps with this.

    Here is a great tutorial.

    This is by far the best option if you want to go for maximum speed: Set up X dart throwers (X = equal to # of cores on the CPU and figured out by the JVM for you), they all throw however many they need to, and you only report the result at the end.