Java Reference
In-Depth Information
AtomicBoolean doneSignal = new AtomicBoolean(false);
Thread printer = new Thread(() -> {
List<String> myPrimes = new ArrayList<>(threads.length);
for (Thread.yield(); !doneSignal.get(); Thread.yield()) {
primes.drainTo(myPrimes);
if (myPrimes.isEmpty()) {
// Take an extra breather
Thread.yield();
} else {
System.out.println(String.join("\n", myPrimes));
myPrimes.clear();
}
}
}
);
printer.start();
for (int t = 0; t < threads.length; t++) {
joinThread(threads[t]);
}
doneSignal.set(true);
joinThread(printer);
}
public static void main(String[] args) {
DemoRunner.run(Listing2::printPrimes);
}
}
The main class just delegates to printPrimes . That method defines our exception handler and creates a
queue for the prime strings. We then launch a thread for each processor. Each of those threads will generate
an even portion of the primes, and put them into a blocking queue. Then we launch another thread whose
sole job it is to read off that queue and print out the values. We need some way to tell that printer thread that
we are done, so we have to declare a signal that the printer is required to monitor on each loop. This whole
thing feels rather like a Rube Goldberg machine.
On my computer, this code runs in about 14.5 seconds, and we are happily saturating all the processors.
However, this code is pretty ugly: we are doing something extremely simple, and there is a bunch of
ceremony and noise obscuring that simple effort. All of that ceremony and noise is necessary because of the
fact that we are working with the Thread class directly. Working with threads this way is incredibly awkward,
which is why Java introduced executors. In the next section, we will see that lambdas help clean those
up, too.
Lambdas and Executors
The ExecutorService and its related classes are convenient wrappers around the Thread class. They provide
the proven and standard patterns of thread execution: there are executors for responsively sized thread
pools, executors for fixed sized thread pools, executors that will generate threads on demand and then cache
them, and executors that wrap a single thread. If you find yourself wanting to do something with threads
fancier than the executors can support, then you are either working on a PhD thesis or doing something
wrong.
 
Search WWH ::




Custom Search