Java Concurrency Concepts

Java Memory Model

  • Describes how threads in Java programming language interact through memory.
  • JVM divides RAM memory between thread stacks and the heap.
    • Thread stack is a secure container contains information on single thread execution of your Java application.
    • Heap contains all objects created in your Java application.

Java memory

  • Stack holds:
    • Local variable which may be:
      • a primitive type, in which case it can be kept on the thread stack. (or)
      • a reference to an object. In that case the reference (the local variable) is stored on the thread stack, but the object itself if stored on the heap.
  • Heap holds:
    • Static class variables are also stored on the heap along with the class definition.
    • Objects on the heap can be accessed by all threads that have a reference to the object.

Volatile Keyword (to avoid invalid object state change among threads)

  • In a multi-core, if two or more threads are sharing an object, without the proper use of either volatile declarations, updates to the shared object made by one thread may not be visible to other threads.
  • Imagine that the shared object is initially stored in main memory. A thread running on Core-1 then reads the shared object into its CPU cache. There it makes a change to the shared object. As long as the CPU cache has not been flushed back to main memory, the changed version of the shared object is not visible to threads running on other CPUs. This way each thread may end up with its own copy of the shared object, each copy sitting in a different CPU cache.
  • To solve this problem you can use Java’s volatile keyword. The volatile keyword can make sure that a given variable is read directly from main memory, and always written back to main memory when updated.

Synchronized (to avoid race conditions among threads)

  • In a multi-core, if two or more threads share an object, and more than one thread updates variables in that shared object, race conditions may occur.
  • Imagine if thread A running in Core-1 reads the variable count of a shared object into its CPU cache. Imagine too, that thread B running in Core-2 does the same, but into a different CPU cache. Now thread A adds one to count, and thread B does the same. Now count has been incremented two times, once in each CPU cache.
  • If these increments had been carried out sequentially, the variable count would be been incremented twice and had the original value + 2 written back to main memory. The two increments have been carried out concurrently without proper synchronization.
  • To solve this problem you can use a Java synchronized block. A synchronized block guarantees that only one thread can enter a given critical section of the code at any given time.
  • Synchronized blocks also guarantee that all variables accessed inside the synchronized block will be read in from main memory, and when the thread exits the synchronized block, all updated variables will be flushed back to main memory again, regardless of whether the variable is declared volatile or not.

Credits: http://tutorials.jenkov.com/java-concurrency/java-memory-model.html

Volatile vs Static

  • Static variables are at the local-thread level or class/ object level, which may be cached by the individual threads.
    • Static variables are stored in heap along with class definition.
    • If one thread modifies it’s cached data, they may not reflect in other threads.
  • Volatile variables are at the global-thread level, which has one main copy shared among all threads.
    • The volatile keyword can make sure that a given variable is read directly from main memory, and always written back to main memory when updated.

https://i.stack.imgur.com/zyhpA.png

ThreadLocal Variable

  • If you have a datum that can vary per-thread, there are two choices:
    • Pass that datum around to every method calls that needs it.
      • This will clutter up your method signatures with an additional parameter.
    • Associate the datum with the thread.
      • This where ThreadLocal used.
  • Many frameworks uses ThreadLocals to maintain some context related to the current thread. For example:
    • When the current ExecutionContextID (eCID) of a web-request is stored in a ThreadLocal, you don’t need to pass it as a parameter through every method call, in case someone down the stack needs access to it.
  • In a non-threaded world, you could solve the problem with the global variable.
  • In a threaded world, the equivalent of a global variable is a thread-local variable.
  • ThreadLocal variables should be always be automatically cleared when they are not needed anymore (i.e., when thread died) to avoid unwanted side-effects and memory leaks in the applications.

Leave a comment