Kotlin Language Features Related to Null Handling

Any software engineer with a Java background would find the null handling features in the Kotlin language interesting. Let's summarize this topic with some examples. Nullable types: In Kotlin, types are non-nullable by default. If you want a variable to be able to hold a null value, you need to explicitly declare its type as nullable using the Type? syntax. For example, String? denotes a nullable string, while String represents a non-nullable string. Safe calls (?.): Kotlin introduces the safe call operator (?.) for handling nullable types. It allows you to safely invoke a method or access a property on a nullable object. If the object is null, the expression returns null instead of throwing a NullPointerException. Example: data class Person(val name: String, val age: Int, val address: String?) fun main() {     // Create a person with a nullable address     val person1 = Person("John Doe", 25, "123 Main Street")     val person2 = Person("Jane Doe", 30,...

Revisiting Thread Safety and Multithreading in Java

One of the most important advice about multi threading is: don't try to improve performance on a particular area of code unless your performance measurements tell you to do so. In some cases, we may need to compromise good object oriented design to gain performance. But most of the time, good object oriented practices like encapsulation and immutability helps us to create thread-safe classes.

A class is thread-safe if it behaves correctly in the case of being accessed by multiple threads and it does not need any synchronization by the caller.

A class with no state (no field, no reference to another class) is thread safe, because it's methods use only local variables which live in the stack. And since each thread have their own stack, a thread cannot mess with a local variable of another.

++ count;

This is not an atomic operation. It does three things and it is an example of read-modify-write operation. The result is dependent on the initial value. But what if the inital value was not up to date? The result will be wrong.

Below is an example of a race condition for a Singleton implementation:

public class NotThreadSafe {
    private ExpensiveObject instance = null;
    
    public ExpensiveObject getInstance() {
        if (instance == null)
            instance = new ExpensiveObject();
        return instance;
    }
}

Here a thread may create the ExpensiveObject but is it visible to other threads? There is no guarantee. So 2 clients may get 2 different ExpensiveObject instances which is not very Singletonish.

One way of ensuring thread safety is by using a synchronized method:
public class SynchronizedFactorizer implements Servlet {
    private BigInteger lastNumber;
    private BigInteger[] lastFactors;
    public synchronized void service(ServletRequest req,
                                     ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        if (i.equals(lastNumber))
            encodeIntoResponse(resp, lastFactors);
        else {
            BigInteger[] factors = factor(i);
            lastNumber = i;
            lastFactors = factors;
            encodeIntoResponse(resp, factors);
        }
    }

}

But here, multiple threads will not be able to work at the same time and this may be a performance problem.

Reentrancy: Locks are acquired on a per-thread basis rather than per-invocation basis. So, when the same thread tries to gather the lock, this is okay. So the following code works just fine: 

public class MovieLister {
    public synchronized void doSomething() {

    }
}
public class AdvancedMovieLister extends MovieLister {
    public synchronized void doSomething() {
        System.out.println("advanced stuff..");
        super.doSomething();
    }
}

Note: Using a synchronized method can be an overkill if the method contains lines that are not a problem for thread safety. So we should try to minimize synchronized parts by using synchronized blocks inside methods. We must also avoid holding locks during lenghty computations.

Memory Visibility Problems:

Lets have a look at the following code:

public class NoVisibility {
    private static boolean ready;
    private static int number;
    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }
    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}

Here the ReaderThread is waiting for the ready flag, and at some point the main method sets it to true. BUT IS IT VISIBLE TO THE OTHER THREAD? There is no guarantee. So in some cases the output of println will be zero, in some cases the thread will not even terminate.

For a single thread, there is "as-if-serial" guarantee. But in reality, the order of the instuctions may change and for other threads there is no guarantee regarding the ordering. 

What is the difference between synchronized collections and concurrent collections?

First of all, both are thread safe. Concurrent collections were introduced with Java 5. More than one threads can read and write concurrently in ConcurrentHashMap and still it provides thread-safety. ConcurrentHashMap divides the Map instance into different segments. And each thread acquires lock on each segment. On the contrary, SynchronizedMap internally wraps all the methods of Map interface with synchronized keyword. So everything is locked and this means a decrease in performance. 



I strongly reccomend this book and some of the examples were taken from that book.




Comments

Popular posts from this blog

Trie Data Structure and Finding Patterns in a Collection of Words

Virtual Memory

NOTES ON COMPUTER ARCHITECTURE: Some important concepts in computer architecture