Skip to main content

Synchronized Keyword in Java

In Java threading support, threads mostly communicate with each other via shared objects or shared member variables within the same object. Three type of complications can arise from this when multiple threads are allowed to access the same piece of memory.

  1. Thread Interference. Different threads access (read and write) the same data. This can lead to race conditions. The program behavior (what gets stored in the shared memory) depends on the order in which threads get access. This can cause non-deterministic behavior.
  2. Memory Consistency Errors. If multiple threads are updating the same variable, they can see a stale (inconsistent) value of a variable.
  3. Thread Contention. If locks are not used correctly, threads can get in each other's way, and slow down or sometimes even have to be killed by Java.

Let's look at the first two problems. If two threads access the same variable, it is possible for them to get in each other's way. That is because Java might switch execution from one thread to another even midway through a simple, seemingly atomic instruction. For example, two threads incrementing the same variable could simply 'loose' one of the two increments.

The solution is to make sure that a section of code that should be atomic is accessed by one thread at a time. Restricting access to an object or a variable - akin to locking the variable so that only one thread can access at a time - is widely used, e.g., in databases.

Locking variables correctly can eliminate the first two problems - Thread Interference and Memory Consistency Errors, but it slows down performance, and can lead to the third problem - Thread Contention issues, namely, Starvation, Livelock, Deadlock, to name a few. This essentially means that a particular thread can no longer make progress.

The Synchronized Keyword

Every object in Java has a lock associated with it. This lock is called the intrinsic lock or monitor. It helps in restricting access to the object. This lock is usually always open, i.e., any number of threads can access the object simultaneously. It is, however, possible to specify that a thread can only execute a section of code once it has acquired the lock on some object. If some other thread currently holds that lock, the current thread must wait its turn.

This ability to give the lock to only one thread is achieved using the "synchronized" keyword. The synchronized keyword is used to activate the intrinsic lock on any object and this lock can be used to restrict access to a section of the code.

Let's see how we may create a counter that is thread-safe. 
public class SynchronizedCounter {
    private int c = 0;
    
    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}
Any method in Java can be marked as synchronized. Doing so means that only one thread can be executing this member function on this object at a given point in time. This means
  • The "only-one-thread-at-a-time" restriction applies to the same method of the same object
  • If the method does something to a static class variable, errors can still result
Marking a method as synchronized is a shortcut to marking the entire body of the method as synchronized on 'this', i.e., the object in question. We can synchronize only a particular block of code using synchronized keyword.
public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

A thread never gets blocked on itself. This means that one synchronized method of an object can always call another synchronized method of the same object without blocking.

In general, any object can be used as a lock using the synchronized statement.

Thread Contention

Synchronization and locks are powerful and they can be misused. Incorrecly used, they can exacerbate thread contention. Some of the thread contention issues manifest themselves into -
  • Deadlock. Two threads, each is blocked on a lock held by the other. For example, if there are two threads - T1 and T2 and two locks - L1 and L2. If T1 holds L1 and T2 holds L2 and T1 needs L2 and/or T2 needs L1 to proceed, they are in a deadlock condition, i.e., they are stalled completely.
  • Livelock. Two threads keep blocking on locks held by the each other repeatedly. 
  • Starvation. Some threads keep acquiring locks greedily, and cause other threads to be unable to get anything done.

Comments

Popular posts from this blog

Supporting OpenTracing jaeger in spring boot applications

This page describes code changes in a typical spring-boot based application to support OpenTracing and jaeger. Instrumenting a tracer If you are creating a simple spring boot application that uses  spring-boot-starter-web , by default, the application does not support writing traces to jaeger. To support jaeger tracing, the first thing is to modify the build.gradle to add dependency of jaeger: dependencies {      implementation  'org.springframework.boot:spring-boot-starter-web'      implementation  'io.opentracing.contrib:opentracing-spring-web-starter:3.0.1'      // support opentracing jaeger      implementation  'io.opentracing.contrib:opentracing-spring-jaeger-starter:3.1.2'      testImplementation( 'org.springframework.boot:spring-boot-starter-test' ) {          exclude group:  'org.junit.vintage' , module:  'junit...

HOWTO on implementing scanning into a CI Pipeline

Introduction As a part of the Software Security Assurance guidelines, we are required to perform various types of security scanning.  Security scanning is a specialized kind of testing that attempts to identify potential software bugs that could make a deployed application vulnerable to attack and compromise.  As with any testing, the tests can be manual or automatic, and they wan be performed at various points in the development cycle.   We can classify security scanning into three general categories: Static Application Security Testing (SAST)   - performed against source code / configuration data a static scan looks for common logic errors that might lead to system compromise. Dy namic Application Security Testing  (DAST)  - Performed against a running system, a dynamic scan looks for vulnerable software / configurations that might lead to system compromise. Security Monitoring  - Deployed as a part of the system, a security monitor co...

Fortify Tooling User Guide

  Introduction The fortify-tools container is located within a shared repository in OCIR and requires a JWT to be able to access.  The variable WF_JWT will need to be set to a valid MAT You will want to choose one of three ways to use the Fortify SCA tooling: Integration Description Using the Fortify Tools Standalone to Scan a Project This is for using the tooling without integration in GitLab CI or Jenkins CI. Using the Fortify Tools In GitLab CI to Scan a Project This is for using the tooling against a project that whose code is hosted in GitLab and whose CI engine is GitLab CI. Using the Fortify Tools In Jenkins CI to Scan a Project This is for using the tooling against a project that whose code is hosted in GitLab and whose CI engine is Jenkins CI. Using the Fortify Tools Standalone to Scan a Project Simple Usage Run the Fortify Tools in a container docker run -t --rm -v <path to project source root directory>:/var/fortify/src phx.ocir.io/oraclegbudevcorp/cn-shared/s...