Skip to main content

Thread Synchronization

   
In Java, thread synchronization is achieved through two constructs: synhcronized methods and synhcronized statements.

int a=7;

void synchronized commonMethod(){
    a+=5;

// additional processing of data not shared among threads       // but unnecessarilly synchronized
}

An example of synchronized methods can be seen above.
int a=7;

void commonMethod(){
    synchronized(this){
        a+=5;
    }
   
    // additional processing of data not shared among threads.    
    // Unnecessary synchronization is avoided.
}

Code fragment above is an example of synchronized statements. Note that synchronized methods may lead to unnecessary synchronization of code segments, while synchronized statements has the flexibility to avoid those situations.

Each object has an internal entity called intrinsic lock, also called monitor lock. A thread which wants exclusive access to object’s resources, in our case instance variables,  has to lock object by acquiring  object’s intrinsic lock.

Once a synchronized method or synchronized statement is reached thread tries to acquire the lock of the object realated to that statement/method. If it can acquire the lock it continues  its execution, threads coming afterwards won’t let inside that statement/method until first thread finishes its job and releases lock. If the lock is already owned by some other thread, newly arrived thread will be blocked until it has chance to acquire lock.

Synronized structs are not only about limiting access to single thread. But they also create memory barries. That is to say, when a thread acquires lock its local momory is invalidated and updated with up-to-date values from main memory. When a lock is released modified values are written to main memory. This way synchronization constructs ensures that all threads synchronizing on the same monitor, see the same memory space.

Note That : When synchronized method is used on an instance method,  lock is acquired on current object instance. When used on static methods lock is acquired on class itself. Same effect can be gained by using synchronized statement and specifying class as argument.
Note That : Once a thread enters a synchronized section no other threads are allowed inside. On the other hand, same thread can enter other syncronized sections of the same object. It can invoke nested synchronized methods, for example. This is called reentrant synchronization.

Note That : Read/Write operations on reference variables and primitive variables other than long and double, Read/Write operations on all volatile variables  are atomic. Altough read/write operations for non-volatile variables are safe  from thread interference, they are prone to memory consistency errors.

Volatile Variables : For volatile variables each read and write is synchronized with main memory and immediately visible to other threads. Order of volatile variables are not changed with other volatile or non-volatile variables. Even more write to volatile variables acst like a lock relaese so that all modified variables in local cache are syncrhonized with main memory. Read on volatile varibales act like a lock acquire,  so that local cahche is reloaded from main memory with fresh values. To sum up, in memory aspect, access to volatile variables acts like synchronized statements.
Consider the example which was used to illustrate memory consistency errors. This time non-volatile field is made volatile:
volatile int a=7;
boolean done=false;

void increment(){
    a=+5;
    done=true;
}

int get(){
    if(done){
        return a;
    }
    return -1;//indicating result is not ready yet
}

Keeping in mind that order of volatile variables remains unchanged and read/write operations are syncronized with main memory together with other variables, reconsider the flow. Thread 1, calling increment method, will always execute increment statement before setting done flage. Thread 2 will always be sure that if done flag is set field a has calculated up-to-date value.

Final Fields:  When an object is constructed, its final fields are initiliazed and will be visible to all threads without need for synchronization provided that object reference is not “leaked” to outside during construction. Consider the following code listing:
class KeyEventListener extends KeyListener{
    final int max;

    public KeyEventListener(JFrame frame,int maxValue){
        frame.addKeyListener(this); // Avoid This
        max=maxValue;
    }
}

Statement marked as “Avoid This” makes object reference visible to outside and final field max becomes accessible to other threads before it is initiliazed. This kind of operations, which reveleas object reference to outide, must be done in seperate methods, not in constructors.  Example below illsutrates how it should be done.
class KeyEventListener extends KeyListener{
    final int max;
   
    public KeyEventListener(int maxValue){
        max=maxValue;
    }
   
    public void register(JFrame frame){
        frame.addKeyListener(this);//Correct way
    }
}


Syhchronized statement or synchronized mehtod?

 Syhcronized methods are good if whole method block needs synchronization. Because they are more neat and method signature indiacates that it is thread-safe. In other cases synchronized statements are preferable. Because they allow more fine grained synronization. With synchronized statements it is possible to lock on other objects.

Further Reading:



Comments

Popular posts from this blog

Obfuscating Spring Boot Projects Using Maven Proguard Plugin

Introduction Obfuscation is the act of reorganizing bytecode such that it becomes hard to decompile. Many developers rely on obfuscation to save their sensitive code from undesired eyes. Publishing jars without obfuscation may hinder competitiveness because rivals may take advantage of easily decompilable nature of java binaries. Objective Spring Boot applications make use of public interfaces, annotations which makes applications harder to obfuscate. Additionally, maven Spring Boot plugin creates a fat jar which contains all dependent jars. It is not viable to obfuscate the whole fat jar. Thus obfuscating Spring Boot applications is different than obfuscating regular java applications and requires a suitable strategy. Audience Those who use Spring Boot and Maven and wish to obfuscate their application using Proguard are the target audience for this article. Sample Application As the sample application, I will use elastic search synch application from my GitHub repository.

Hadoop Installation Document - Standalone Mode

This document shows my experience on following apache document titled “Hadoop:Setting up a Single Node Cluster”[1] which is for Hadoop version 3.0.0-Alpha2 [2]. A. Prepare the guest environment Install VirtualBox. Create a virtual 64 bit Linux machine. Name it “ubuntul_hadoop_master”. Give it 500MB memory. Create a VMDK disc which is dynamically allocated up to 30GB. In network settings in first tab you should see Adapter 1 enabled and attached to “NAT”. In second table enable adapter 2 and attach to “Host Only Adaptor”. First adapter is required for internet connection. Second one is required for letting outside connect to a guest service. In storage settings, attach a Linux iso file to IDE channel. Use any distribution you like. Because of small installation size, I choose minimal Ubuntu iso [1]. In package selection menu, I only left standard packages selected.  Login to system.  Setup JDK. $ sudo apt-get install openjdk-8-jdk Install ssh and pdsh, if not already i

Java: Cost of Volatile Variables

Introduction Use of volatile variables is common among Java developers as a way of implicit synchronization. JIT compilers may reorder program execution to increase performance. Java memory model[1] constraints reordering of volatile variables. Thus volatile variable access should has a cost which is different than a non-volatile variable access. This article will not discuss technical details on use of volatile variables. Performance impact of volatile variables is explored by using a test application. Objective Exploring volatile variable costs and comparing with alternative approaches. Audience This article is written for developers who seek to have a view about cost of volatile variables. Test Configuration Test application runs read and write actions on java variables. A non volatile primitive integer, a volatile primitive integer and an AtomicInteger is tested. Non-volatile primitive integer access is controlled with ReentrantLock and ReentrantReadWriteLock  to compa