неделя, 16 октомври 2011 г.

Java Concurrent API

If you have read the previous part of the article series, you should be aware that even the simplest operations like incrementing a counter require your attention(you must think where to put volatile or synchronized). That's why Java Concurrent API was created,  where some common operations and patterns are implemented as atomic and thread-safe. All classes from this API are contained in the java.util.concurrent package.

The first part of classes which we will take a look at, are in the java.util.concurrent.atomic sub-package. They implement thread-safe operations on single variables. So let's see how we can transform the counter example from the previous article using the API classes:
public class IdCounter {
    private AtomicInteger counter = new AtomicInteger();

    public int increment() {
        return counter.incrementAndGet();
    }
}
So AtomicInteger basically wraps a primitive integer value and allows you to make thread-safe operations on it. You don't care anymore about volatile and synchronized. You just use the appropriate methods. The other classes in the java.util.concurrent.atomic package are basically doing the same but not on a different type - Long, Boolean, Array, etc..(AtomicLong, AtomicBoolean, AtomicIntegerArray).

All these classes have the useful method compareAndSet(expectedValue, newValue). It is making atomic the following kind of code:
if (variable == expectedValue) {
   variable = newValue;
}
Without appropriate synchronization the above code is error prone to race conditions, because while one thread is setting the newValue, a second thread can be making the comparison with the expectedValue. This is not what you want in the most cases.


Now it is time to take a look at the Producer-Consumer pattern and how the concurrent API makes its implementation a piece of cake. There are two roles here - the producer and the consumer. The producer produces work and the consumer consumes work, created by the producer. Imagine a factory conveyer. A machine puts some products on the conveyer. Then a person gets the product from the conveyer and do whatever is needed next. Here the machine is the producer and the person is the consumer. In programming terms - the conveyer is a buffer, the producer and consumer are two separate threads which are inserting and getting information from the shared buffer. As you know extra care must be taken when two threads are accessing a shared resource. Hopefully the concurrent API provides the BlockingQueue interface. There are the methods put and take which "produce" and "consume" from the queue. Both methods are blocking, which means they will block(wait) until there is an empty space for producing, or until at least one product appears for consuming. Here is a simple example of a Producer and Consumer class. Exception handling is left off for clarity.
public class Producer implements Runnable {
    private BlockingQueue queue;

    public Producer(BlockingQueue queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        //Produce 1000 products
        for (int i = 0; i < 1000; i++) {
            queue.put(new Product());
            System.out.println("PRODUCED PRODUCT");
        }
    }
}
public class Consumer implements Runnable {
    private BlockingQueue queue;

    public Consumer(BlockingQueue queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            Product product = queue.take();
            System.out.println("CONSUMED PRODUCT");
        }
    }
}

And here is the calling code:
BlockingQueue queue = new ArrayBlockingQueue(50);
        
Producer producer = new Producer(queue);
new Thread(producer).start();
        
Consumer consumer = new Consumer(queue);
new Thread(consumer).start();

Here the limit of the queue is 50, which means there can be maximum 50 products at the same time in the queue. Also ArrayBlockingQueue is used as an implementation of BlockingQueue, which means that the queue is backed by an array.

Thanks for reading the second part of the article series!

Stay tuned for the next part where other points from Java Concurrent API will be covered - ExecutorService and Semaphore.

неделя, 9 октомври 2011 г.

Thread articles

I am starting a series of articles with topics based on threads and thread-safety.

Thread safety, volatile and synchronized

In today's world we can barely write a complex app, without using more than one thread. You know - the user interface should not freeze, your computer has more than one core and you decide to use their full power, etc... The point is that as long as a second thread enters into the picture, we the programmers are out of the comfort zone. The program flow is not so ordinary anymore. If you are somewhat newbie to threads, you will ask:  

1. What is it all about the thread safety?
Thread safety is all about shared state - state, used by more than one thread simultaneously. This is all you have to care about. For example a variable used by more than one thread. Sounds simple enough, right?

Since an example is worth a thousand words, take a look at this class:
public class LongRunningTask() {
      private boolean shouldStop;

      public void work() {
           while (!shouldStop) {
                 launchRocketToTheMoon();
           }
      }

      public void stop() {
           shouldStop = true;
      }
}

Let's consider that 100 rockets were launched to the moon and you are tired of it. You call the stop() method to stop the process, which was running in a different thread. The scary thing here is that there isn't a guarantee that your program will stop launching rockets at all. You will ask why?

Lesson learned 1: If two threads are accessing the same variable, there isn't any guarantee that when the first thread makes a change to the variable, the second thread will see it.

The reason is that caching can be applied for greater efficiency at different levels and the second thread will receive a stale data. So, what is the solution?

2. Apply the volatile modifier to the variable.
When using the volatile modifier you are basically telling - give me the latest value from the RAM, don't even try to get it from a cache.


The following version of the class, fixes the above defficiency:
public class LongRunningTask() {
      private volatile boolean shouldStop;

      public void work() {
           while (!shouldStop) {
                 launchRocketToTheMoon();
           }
      }

      public void stop() {
           shouldStop = true;
      }
}

Lesson learned 2: Use the volatile keyword to prevent receiving stale data.

At this level, you may say: Ah clear, so we put the volatile behind every variable, used by more than one thread, and we are fine with the thread-safety. Totally wrong. With using the volatile modifier you only guarantee that you won't be accessing cached stale data. This can be sufficient only in the most basic cases(like the rocket example, discussed above). But in more complex situations, the so called race conditions come to bite you.

3. Race conditions and the synchronized keyword:

Take a look at this simple counter class:
public class IdCounter {
     private volatile int counter;

     public int increment() {
          counter++;
          return counter;
     }
}

Although the class looks innocent, it will fail miserably if used by more than one thread simultaneously. If you run two threads and call the increment method from them, there is a very high chance you won't get the sequence 1, 2, 3, 4, 5, 6, ... but something different like 1, 2, 2, 3, 4, 5, ...

You ask: "Why that? I am not receiving stale data. I am using volatile."

Let's take a closer look at the counter++ line. It is equivalent to counter = counter + 1. This simple line is doing three things. First it gets the old value of the counter, adds 1 to it and returns the new value, which is set to the counter. Here is the catch: If the first thread is getting the old value, and at the same time the second thread is getting the old value, then counter = oldValue + 1 = 1 + 1 = 2, and counter = oldValue + 1 = 1 + 1 = 2. So we called increment two times and received the same number. We are totally screwed. This is a classical example of a race condition. The two threads are participating in a race and depending on their position in the race, different unpredictable things can happen. Race conditions are difficult to debug because they are not reproducible. You run the code for the first time - it works correctly. You run the code for the second time - it doesn't work correctly. It is a nightmare.

So how to solve the problem? The synchronized keyword comes to the rescue.
The synchronized keyword can be used to guard both a whole method or only a block of code.
When you apply synchronized to a method, then when a second thread tries to enter the same method, it will be waiting there for the first thread to exit from the method.

Here is the fixed thread-safe code:
public class IdCounter {
     private int counter;

     public synchronized int increment() {
          counter++;

          return counter;
     }
}

When one thread calls the increment() method, and then a second thread calls it, the second thread will wait for the first one to exit from the method, before entering it. So there is no danger for the counter now. The increment operation is now atomic. Notice also that the volatile modifier was removed. This is because when you are in a synchronized method or block, then no caching of variables is used. So synchronized is also doing what volatile does.

Lesson learned 3: Use the synchronized keyword to prevent race conditions.

Stay tuned for the second part of the article where Java Concurrent API will be touched, and also some more complex examples will be covered.

неделя, 4 септември 2011 г.

The point of the blog

I am starting this blog mainly to share interesting stuff about programming in general. You know - new technologies, design patterns, multi-threading, nice algorithms, tricks, frustrating bugs or whatever about programming:) I sincerely hope some of my posts will be helpful for you, my readers.