Core Java SE9 for the Impatient

Chapter 5 - Exception Handling

Try-With-Resources, Caused By and Suppressed Exceptions

What happens if an exception is thrown inside a try block, and afterwards again an exception is thrown when close is called? Try-with-Resources is smart enough to add the exception thrown within close as a suppressed exception to the exception thrown within the try. Here is an example:

class MyAutoClosable implements AutoCloseable {

    void foo() {
        try {
            bar();
        } catch (NumberFormatException e) {
            throw new UnsupportedOperationException("foo failed!", e);
        }
    }

    void bar() {
        throw new NumberFormatException("bar failed!");
    }

    @Override
    public void close() {
        throw new IllegalStateException("close failed!");
    }
}

And executing the code above:

try (MyAutoClosable myAutoClosable = new MyAutoClosable()) {
    myAutoClosable.foo();
}

Output will be:

Exception in thread "main" java.lang.UnsupportedOperationException: foo failed!
    at biz.tugay.MyAutoClosable.foo(MyAutoClosable.java:9)
    at MyMainClass.main(MyMainClass.java:6)
    Suppressed: java.lang.IllegalStateException: close failed!
        at biz.tugay.MyAutoClosable.close(MyAutoClosable.java:19)
        at MyMainClass.main(MyMainClass.java:7)
Caused by: java.lang.NumberFormatException: bar failed!
    at biz.tugay.MyAutoClosable.bar(MyAutoClosable.java:14)
    at biz.tugay.MyAutoClosable.foo(MyAutoClosable.java:7)
    ... 1 more

Exception thrown within close call is suppressed. Exception passed into the UnsupportedOperationException as cause is printed. Not all Exception classes have a constructor that takes a parameter to store the cause. In that case you need to call the initCause method.

To learn more about suppressed exceptions, read this answer.

Finally Block

Sometimes, you need to cleanup a resource that is not AutoClosable. In that case use the finally block as follows:

try {
    // do work...
} finally {
    // clean up
}

Things to keep in mind when using the try - finally pattern:

Lets change the example above slightly:

class MyAutoClosable implements AutoCloseable {

    void foo() {
        try {
            bar();
        } catch (NumberFormatException e) {
            throw new UnsupportedOperationException("foo failed!", e);
        }
    }

    void bar() {
        throw new NumberFormatException("bar failed!");
    }

    @Override
    public void close() {
        throw new IllegalStateException("close failed!");
    }
}

and instead of using try-with-resources, lets use try - finally approach:

MyAutoClosable myAutoClosable = new MyAutoClosable();
try {
    myAutoClosable.foo();
} finally {
    myAutoClosable.close();
}

And the output will be:

Exception in thread "main" java.lang.IllegalStateException: close failed!
    at biz.tugay.MyAutoClosable.close(MyAutoClosable.java:19)
    at MyMainClass.main(MyMainClass.java:9)

Danger! This is not most likely what we want.. We are only made aware of that close has failed. The exception thrown in foo is lost, gone to nirvana. I think this is one way to solve the problem:

MyAutoClosable myAutoClosable = new MyAutoClosable();
Exception exception = null;
try {
    myAutoClosable.foo();
} catch (Exception e) {
    exception = e;
} finally {
    try {
        myAutoClosable.close();
    } catch (Exception e) {
        if (exception != null) {
            exception.addSuppressed(e);
        } else {
            exception = e;
        }
    } finally {
        if (exception != null) {
            throw exception;
        }
    }
}

Exercises

Exercise 8

If input fails when using a Scanner, the Scanner class catches the exception and stops reading from the input source. What happens when the scanner is closed, and closing the input source throws an exception?

It seems like Scanner instead of adding a suprressed exception, overrides the exception thrown in reading, and only keeps the exception thrown while closing the resource:

public void close() {
    if (closed)
        return;
    if (source instanceof Closeable) {
        try {
            ((Closeable)source).close();
        } catch (IOException ioe) {
            lastException = ioe;
        }
    }
    sourceClosed = true;
    source = null;
    closed = true;
}

Here is how I was able to reproduce this behaviour:

class MyReadableClosable implements Closeable, Readable {
    @Override
    public int read(CharBuffer cb) throws IOException {
        throw new IOException("read failed!");
    }

    @Override
    public void close() throws IOException {
        throw new IOException("close failed!");
    }
}

and the code to test behaviour:

Scanner scanner = new Scanner(new MyReadableClosable());
while (scanner.hasNext()) {
    scanner.next();
}
scanner.close();

if (scanner.ioException() != null) {
    scanner.ioException().printStackTrace();
}

Output will be:

java.io.IOException: close failed!
    at biz.tugay.MyReadableClosable.close(MyReadableClosable.java:15)
    at java.util.Scanner.close(Scanner.java:1093)
    at MyMainClass.main(MyMainClass.java:11)

and we did actually lose the important information, that actually read failed.. It is also not cool that Scanner is swallowing IOException that is declared in Closable#close and storing it in lastException. Some very strange design this class has!

Chapter 10 - Concurrent Programming

ExecutorService

Runnables and Callables represent tasks that will run in separate threads. It does not make sense to have a 1:1 relationship between a task and a thread, in other words it does not make sense to spawn a new Thread for every task just to terminate it later.

In the Java concurrency library, an ExecutorService schedules and executes tasks and allocates threads to them.

Runnables

A Runnable can either be executed or submitted.

ExecutorService executorService = Executors.newFixedThreadPool(2);

executorService.execute(myRunnable);
executorService.execute(anotherRunnable);

These calls will not block the execution. There are several options to wait for Runnables to complete, a simple one being calling shutdown followed by awaitTermination:

executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);

Callables

Callables are Runnables on steroids that return Futures which wraps the returning value of the task. Callables are submitted:

Future<MyCallableReturnType> future = executorService.submit(myCallable);
MyCallableReturnType val = future.get();  // blocks current thread

It is also possible to gracefully wait on a Future by making use of the isDone method:

ExecutorService es = Executors.newFixedThreadPool(2);

Callable<Integer> callable = () -> {
    // Long running task..
    Thread.sleep(4000);
    return 42;
};

Future<Integer> future = es.submit(callable);

System.out.print("Processing..");
while (!future.isDone()) {
    Thread.sleep(1000);
    System.out.print(".");  // Feedback to user application did not freeze
}

System.out.println(future.get());  // Print the result

es.shutdown();

CompletableFutures

CompletableFutures are Futures on steroids that accept callbacks instead of returning a wrapped result.

ExecutorService es = Executors.newFixedThreadPool(2);

CompletableFuture.supplyAsync(() -> {
    try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
    return 42;
}, es).whenComplete((integer, throwable) -> System.out.println(integer));

Thread Safety

Visibility

What happens when you run the program below? How do you make it actually complete instead of running forever?

static boolean done = false;

public static void main(String[] args) throws Exception {
    ExecutorService es = Executors.newCachedThreadPool();

    es.execute(() -> {
        for (int i = 0; i < 100; i++) {
            sleepOneMilliSecond();
        }
        System.out.println("Finished.");
        done = true;
    });

    es.execute(() -> {
        while (!done) {}
        System.out.println("I can continue.");
    });

    es.shutdown();
}

static void sleepOneMilliSecond() {
    try { Thread.sleep(1); } 
    catch (InterruptedException ignored) {}
}

Race Conditions

Race conditions happen whenever a value is shared between threads. Here is an example:

static volatile int count = 0;

public static void main(String[] args) throws Exception {
    ExecutorService es = Executors.newCachedThreadPool();

    es.execute(() -> {
        for (int i = 0; i < 1000; i++) {
            sleepOneMilliSecond();
            count++;
        }
    });

    es.execute(() -> {
        for (int i = 0; i < 1000; i++) {
            sleepOneMilliSecond();
            count++;
        }
    });

    es.shutdown();
    es.awaitTermination(1, TimeUnit.MINUTES);  // wait for tasks to complete

    System.out.println(count);
}

static void sleepOneMilliSecond() {
    try { Thread.sleep(1); } 
    catch (InterruptedException ignored) {}
}

Will the printed value be 2000?

Locks and Conditions

notifyAll Example

Example below makes use of the notifyAll method. 2 threads, a and b both acquire the lock, and start waiting on them. Main thread acquires the same lock, sleeps for 2 seconds and then calls notifyAll. We can observe both threads waiting on the locks proceeding after main thread calls notifyAll.

Object lock = new Object();
CountDownLatch countDownLatch = new CountDownLatch(2);

Thread a = new Thread(() -> {
    synchronized (lock) {
        try {
            countDownLatch.countDown();
            System.out.println("First thread is sleeping.");
            lock.wait();
            System.out.println("First thread continue.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

Thread b = new Thread(() -> {
    synchronized (lock) {
        try {
            countDownLatch.countDown();
            System.out.println("Second thread is sleeping.");
            lock.wait();
            System.out.println("Second thread continue.");
            lock.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

a.start();
b.start();

// Do not continue unless threads a and b start
countDownLatch.await();

synchronized (lock) {
    System.out.println("Main thread is sleeping.");
    Thread.sleep(2000);
    System.out.println("Notifying all.");
    lock.notifyAll();
}

// Wait for both threads to complete
a.join();
b.join();
System.out.println("Goodbye!");

Threads

ThreadLocal

ThreadLocal<T> helper class can be used to keep data scope to a single thread only. Here is an example:

class UserContext {
    private static ThreadLocal<String> currentUserId = new ThreadLocal<>();

    static String getCurrentUserId() {
        return currentUserId.get();
    }

    static void setCurrentUserId(String currentUserId) {
        UserContext.currentUserId.set(currentUserId);
    }
}

currentUserId can safely be used among different threads by:

UserContext.setCurrentUserId(userId);
UserContext.getCurrentUserId();

Another example can be presented using NumberFormat class, which is not thread safe. NumberFormat instances can be shared between threads as follows then:

static ThreadLocal<NumberFormat> currencyFormat
    = ThreadLocal.withInitial(NumberFormat::getCurrencyInstance);

Every thread can call the following safely:

currencyFormat.get().format(aNumber);

The first time you call get in a given thread, the lambda expression in the constructor is called to create the instance for the thread. From then on, the get method returns the instance belonging to the current thread.

Chapter Notes

Exercises

How large does an array have to be for Arrays.parallelSort to be faster than Arrays.sort on your computer?

Hard to tell..

Sorting 131072 integers..
Array Sort:8
Parallel sort:2

Sorting 65536 integers..
Array Sort:4
Parallel sort:1

Sorting 32768 integers..
Array Sort:2
Parallel sort:1

Sorting 16384 integers..
Array Sort:1
Parallel sort:0

Sorting 8192 integers..
Array Sort:1
Parallel sort:0

Sorting 4096 integers..
Array Sort:0
Parallel sort:1
Parallel sort started losing!

When parallelSort starts losing, the number of items sorted are so little, benchmark starts making no sense. parallelSort starts becoming significantly faster even around 250k items:

Sorting 262144 integers..
Array Sort:22
Parallel sort:3

Write an application in which multiple threads read all words from a collection of files. Use a ConcurrentHashMap<String, Set<File>> to track in which files each word occurs. Use the merge method to update the map.

In order to solve this problem I created 10 lorem ipsum files and dumped them in /Users/kt/lorem. Here is my solution:

Map<String, Set<File>> wordFileMap = new ConcurrentHashMap<>();
List<Path> loremFiles = 
    Files.list(Paths.get("/Users/kt/lorem")).collect(toList());

ExecutorService es = Executors.newFixedThreadPool(10);
loremFiles.forEach(path -> es.submit(() -> {
    try {
        File file = path.toFile();
        Scanner scanner = new Scanner(file, StandardCharsets.UTF_8.name());
        scanner.useDelimiter("\\W+");
        while (scanner.hasNext()) {
            String word = scanner.next();
            word = word.trim();
            wordFileMap.merge(word,
                    new HashSet<>(singletonList(file)),
                    (existing, found) -> {
                existing.addAll(found);
                return existing;
            });
        }
        scanner.close();
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}));
es.shutdown();
es.awaitTermination(1, TimeUnit.MINUTES);

// Checksum to be used when trying for different approaches
// Calculate checksum by sorted keys and sorted values
StringBuilder sb = new StringBuilder();
wordFileMap.keySet().stream().sorted().forEach(s -> {
    String files = wordFileMap.get(s).stream()
            .map(File::toString)
            .sorted().collect(Collectors.joining());
    sb.append(s);
    sb.append(files);
});

MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] md5 = digest.digest(sb.toString().getBytes(StandardCharsets.UTF_8));
System.out.println(DatatypeConverter.printHexBinary(md5).toLowerCase());

This approach seems to be thread safe, making use of the merge method. I also tried making use of putIfAbsent but turned out to be not thread safe. See this discussion for details.


🏠