Friday, 23 March 2012

logback - successor of log4j

The old and good known log4j seems to be a standard framework for logging in Java applications, despite some serious disadvantages like boilerplate configuration, lack of good documentation and overcomplicated architecture.

The authors of log4j dispatched on another journey and created its successor - logback, which addresses old problems and adds a lot of enhancements. 

Configuration is now more concise (it can be even written in groovy) and well documented.
slf4j api is used natively, so the implementation can be changed easily.
The issue with many instances of RollingFileAppender writing to the same file was also resolved.

In order to add logback to your project you need to add two dependencies:

You're basically ready to go, because default configuration is applied, when no other is found.

When following class is run:

it will print something like this:

But the coolest feature is automatic reloading of configuration file.

When we add this example configuration (saved as logback.xml) to the classpath:

we can change it on the fly and logback will automatically apply changes (at configured interval, in our case 5 seconds) without a need to restart the application.

Sunday, 18 March 2012

Getting result from thread's execution with Future

To make things go faster we parallelize our computations. What if we need to use the result of the thread's execution?
Let's say we have a service that buys some product. It needs to fetch the price and quantity of the product. Fetching the price usually takes longer, so we delegate this task to a thread, while we are dealing with quantity.
To keep things simple, our PriceChecker class will be just simulating that it does something meaningful:

Now it would be good to somehow get the result of checkPrice() invocation. Runnable's run() is a void method, so we would have to do something like this:

This approach has a lot of drawbacks. We have to check in the loop if the price has already been set. What is more, price cannot be a final variable but has to be a field instead.

To deal with this kind of a problem, the Future interface should be used. Basically, it allows to get the result of thread's execution. Let's take a look at the actual usage in the context of our example:
First of all we're using Callable which is similar to Runnable but is capable of returning the result. Notice that it can also throw exception, while run() cannot. When we submit the callable to executor service, we're getting the Future object. Its method get() blocks until the computation is finished. If we want to specify the maximum time that we want to wait, there is an overloaded version that takes waiting time settings as parameters.

The runner for both cases is pretty straightforward:

Saturday, 10 March 2012

Handling read and write operations on shared object

Many times we come across a problem, when in the multithreaded environment some shared object has frequent read operations and write operations are rare. If we could use a concurrent collection that would be the best option in most of the cases, but sometimes we can't.

Let's imagine a situation when this shared object is in a 3rd party library. In our example this object is a Book class that I used in previous posts.

Most of the time we want to show information about the book, but exceptionally the author modifies the content and therefore the number of pages needs to be changed.

Using synchronized for the methods that read/write the book would be a poor solution.
Instead, we could use ReadWriteLock which allows to differentiate between read and write operations. Read operation is concurrent, which means that all the threads have access to the object. Only write operation is exclusive and blocks other threads.

Everything we do after acquiring the lock must be put in a try block, so that unlocking is done even if the exception is thrown.

Let's see it in action:

We have 10 threads that are performing 100 tasks. Only 10% of them are write tasks. When we run it, we can see that read operations are concurrent (lines about the reading thread and book info are mixed from time to time) and write operations are exclusive (info about writing thread and successful update are always in the correct order).

The example with the book is purely educational and not too realistic. In real life we would probably have some kind of a large collection. It's good to read ReentrantReadWriteLock and ReadWriteLock javadoc, because sometimes using them may bring more overhead than mutual exclusion.