Wednesday, April 27, 2016

Java Multithreading in Practice: Part 2

In the previous Java Multithreading in Practice we discussed simplifying the base Java API for increased readability and reliability.  However, after further analysis catching exceptions was still not full-proof.  The end-user (of the API) would still need to remember to catch Exception in their Runnables and Callable<Void>s.  A working solution is to automatically do that internally in the custom ThreadPool class (not Java's ThreadPool class) with two protected methods, toSafeRunnable(Runnable) and toSafeCallable(Callable<Void>).  All these do is wrap the passed in argument in try catch block, catch Exception and log it as an error.  This lets the end user not have to worry about exceptions being lost in the system.

PS

In-line code is now monospaced and green to let it stand out. I hope that this helps people read the blog!

Wednesday, April 20, 2016

Java 8 Multi-Threading in Practice

Dealing with multithreading and race conditions is a famously hard and tricky subject for computer science.  On top of that, Java has some odd and unexpected implementation details in this area as well.

The first thing I noticed is that my engine was taking over 40 seconds to shut down after a Ctrl-C.  Where was the interruption going?  It turns out that the forEach lambda, apparently as part of it's functionalness, completely ignores interruptions until it is completely done with its tasks.  This means that the new stream APIs are not useful for long-running tasks (e.g. the kinds of tasks you want to run in parallel in the first place) without a work-around, such as checking manually if a thread is interrupted or a thread pool is shutdown (see below).

The second thing I noticed that was when a Callable was being executed by and the executor service was shut down, the Callable's Thread.currentInstance().isInterrupted() would return false, even if actively checked!  As a work-around I had to pass a reference to the executor service into the Callable and check ExecutorService.isShutdown() instead.  In my particular code-base this caused a circular reference but the modern garbage collector uses mark-and-sweep instead of reference counting so no memory leaks happen.
It could be worse [1].
ExecutorService.invokeAll()

Lastly the APIs can't decide if they want Runnables or Callables.  ExecutorService.invokeAll() only takes Callables and ScheduledExecutorService.scheduleAtFixedRate() only takes Runnables.  Of course I have the same logic that sometimes needs to be run all at once and sometimes needs to be run repeatedly so I need to convert between the two.  It looks like if an exception does bubble out of a Runnable.run() call, it is simply ignored.  This caused some strange system behavior until I made a habit of catching Exception and logging an error.  Of course, we hate to catch Exception.  For this reason I strongly prefer Callable, despite the debate on StackOverflow.

The solution was to create a ThreadPool class and ScheduledThreadPool sub class in my project's utils module to simplify all of this complexity and automatically convert between Runnables and Callables as needed.  After I made these classes and had the code base use them it was easier to think about how the system worked instead of being mired in the details of the Java API.

[1] Dragon Riders of Pern by FanDragonBall

Wednesday, April 13, 2016

LocalDateTime Is Not Local!

No one ever has problems with Dates and Times!  (Y2k, 2038, 10,000)

Java8 dates and times were supposed to be a lot easier and designed by the Joda-Time guys.

One problem, after struggling for a full working day - LocalDateTimes are NOT Local!  Maybe in the sense of localized?  They are definitely UTC, not your local timezone.  Looking back at it, it says that it doesn't capture timezone information in the docs, but I didn't look at the docs because I thought that the name was blindingly obvious.

Copyright BBC
I was pulling my hair out when I wrote a function todayAt that returned a date that didn't register as today.  A time at 3:45 PM was being converted to 2 AM UTC (the next day) because LocalDateTime wasn't local.  I was looking for ZonedDateTime.

As they say, there are two hard things in Computer Science: caching, naming things and off by one errors.

Tuesday, April 5, 2016

What's the Goal of AI? Tai.ai and Bad Decisions

The implicit, if not explicit goal of artificial intelligence (AI) has been to create "human-like" intelligence.  The recent debacle of Tay.ai is the first time something has seemed to become strangely close.

That is to say, the aim of the project was to simulate someone in their late teens.  People in their late teens tend to make poor decisions, especially to go along with what people around them believe.  (I'm no exception and had quite a long "hippie phase" including being Vegan for 3 years.)
Vegan super-powers from Scott Pilgrim vs. the World


Do we really want to make an AI like us, with all of our foibles and cognitive distortions or are we going to pull the plug as soon as one adopts unconventional, minority or fringe views?  Will we use the phrase "it's just going through a phase"?

People do dumb stuff and usually learn from it and course correct... sometimes they make the same mistakes for decades on end until the very end.

I would think that if we are doing it right we will have a crop of AIs just as diverse as ourselves, including the bad stuff.

PS

I was in the hospital so missed a week or two of Tuesday posts.  It happens.