- Work with a buddy
- One of you codes, the other types up the answers.
- One of you submits the code in the
`lab26`

subdirectory of your personal repo, the other submits a file`report.txt`

in the`lab26`

subdirectory of your personal repo. - Put the name of the coding buddy into the report

- Make a new project
`lab26`

and add a class`Primes`

with this method:public static Runnable printPrimes(BigInteger start, long length) { return () -> { BigInteger n = start; for (long i = 0; i < length; i++) { if (n.isProbablePrime(100)) System.out.println(n); n = n.add(BigInteger.ONE); } }; }

What does the method do? (Note:`n.isProbablePrime(100)`

returns true if`n`

is a prime, with error probability < 2^{-100}. Assume for now that means that`n`

is certainly a prime.) - Make two
`Runnable`

objects like this:Runnable r1 = printPrimes(new BigInteger("1000000000000000"), 10000); Runnable r2 = printPrimes(new BigInteger("2000000000000000"), 10000);

- Run them in parallel:
ExecutorService service = Executors.newFixedThreadPool(2); ... ... service.shutdown();

Run your program. What happens? - How can you tell that the two tasks ran concurrently?

- Now we want to know how many primes are in a given interval instead of printing them to
`System.out`

. We need to use a`Callable`

since a`Runnable`

can't return a value. Make a function`countPrimes`

that returns a`Callable<Long>`

. - Make two callables
Callable<Long> c1 = countPrimes(new BigInteger("1000000000000000"), 500_000); Callable<Long> c2 = countPrimes(new BigInteger("1000000000500000"), 500_000);

- Submit them both to the same
`ExecutorService`

as in the preceding slide. You'll get two`Future<Long>`

. CallSystem.out.println(f1.get()); System.out.println(f2.get());

before callingservice.shutdown();

Run the program. What does it print? - Run it again. Does it print the same thing?
- The
`isProbablePrime`

method sounds as if it was guessing, but it is actually perfectly deterministic. For a given`n`

, the call`n.isProbablePrime(100)`

will always give the same result. It is just that the result may be wrong. The chance for that is 2^{-100}or about 10^{-30}. The probability of you being struck by lightning in a given year is about 10^{-6}. Now imagine yourself and four other people—your lab buddy, another pair of buddies, and your long-suffering professor. The probability of*all of us five*being struck by lightning*in the same year*is about 10^{-30}. If that what keeps you up at night, then you should definitely worry about`n.isProbablePrime(100)`

giving you the wrong answer. - Let's find out if running the two tasks in parallel does any good. Add these calls around the calls to
`service.submit`

long start = System.currentTimeMillis(); ... long end = System.currentTimeMillis(); System.out.println("Milliseconds: " + (end - start));

Run the program and write down the number of milliseconds. Then change`Executors.newFixedThreadPool(2);`

to`Executors.newFixedThreadPool(1);`

, which means that only one thread is available. Run the program again. You should notice a delay between the printouts of the two counts. What milliseconds do you get? - Did you get the same counts in both the faster and the slower run?

- Now let's compute the count differently. We'll just increment a shared counter. Add a field
private static long nonprime = 0;

In the`countPrimes`

method, addelse nonprime++;

when a number isn't a prime. After printing each result, add a callSystem.out.println(nonprime);

Run the program a few times. What results do you get? Which values are the same, and which are different in each run? - As you can see, incrementing a counter from two threads doesn't work reliably—in other words, it doesn't work. We'll see in the next lecture how you can fix that. But you already know a fix, namely not to do it in the first place. How could you have safely computed the total number of non-primes?