OCP Question 8, Explanation

Given the code fragment:

class Test implements Runnable {
    String fileName;
    public Test(String fileName) { this.fileName = fileName; }
    public void run() {System.out.println(fileName);}
    public static void main (String[] args) throws IOException, InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        Stream<Path> listOfFiles = Files.walk(Paths.get("C:\\Try_Java"));
        listOfFiles.forEach(e -> {
            exec.execute(new Test(e.getFileName().toString()));          // line n1
     });
        exec.shutdown();
        exec.awaitTermination(365, TimeUnit.DAYS);                       // line n2
    }
}

The C:\Try_Java directory exists and contains a list of files. What is the result?

A. The program throws a runtime exception at line n2.
B. The program prints files names concurrently.
C. The program prints files names sequentially.
D. A compilation error occurs at line n1.

 

The correct answer is option B.

Short explanation: ExecutorService uses available threads concurrently.

Verbose explanation:

Borrowing from Oracle’s tutorial on parallelism: “When you create a stream, it is always a serial stream unless otherwise specified”, so it may appear that unless we split our stream into parallel substreams (by inserting .parallel() before .forEach()), the code will be outputting file names in a rigid order, traversing folders from inside out one at a time. But thinking so would be a mistake.

To see why, let’s take a look at the ExecutorService object that we’re creating (ref.to the javadoc on Executors):

So if the specified directory contains just a couple of files, chances are high for an illusion that the code runs sequentially but on a larger folders the filenames will be definitely printed in a chaotic fashion.

In order to see the list of filenames in the same order over and over again, we would have to switch from cached thread pool to a fixed-size thread pool and additionally restrict the number of available threads to only one:

ExecutorService exec = Executors.newFixedThreadPool(1);

Speaking about option D, line n1 doesn’t flag a comperr because the execute() method was provided a properly formed argument: the Test class IS-A Runnable, and Path’s getFileName() returns a Path, which contains the current filename.

Line n2 cannot throw an InterruptedException because the executor is question has been already terminated, so this call simply returns true as per the javadoc on awaitTermination().

On a final note, I would like to point out that this question was improperly coded, and if used in real life might well botch the job and even compromise your file system’s performance. The javadoc on Files.walk() clearly states that “If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream’s close() method is invoked after the stream operations are completed.” The question, however, took care of the CSR by declaring that main() throws. To see this in action, change the Path.get()’s arg to “C:\\Program Files” or something equally huge and observe all those nasties jumping at you.

Okay, the last piece was well beyond the Exam Objectives but here I have for you an additional question that falls within 1Z0-809’s scope:

Where is the abstract execute(Runnable command) method declared?

  1. In the Executor interface
  2. In the Runnable Interface
  3. In the Thread class

Click here to view the correct answer.

Leave Comment

Your email address will not be published. Required fields are marked *