DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Redefining Java Object Equality
  • Singleton: 6 Ways To Write and Use in Java Programming
  • Creating a Deep vs. Shallow Copy of an Object in Java
  • Generics in Java and Their Implementation

Trending

  • Kubeflow: Driving Scalable and Intelligent Machine Learning Systems
  • Building Enterprise-Ready Landing Zones: Beyond the Initial Setup
  • Mastering Fluent Bit: Installing and Configuring Fluent Bit on Kubernetes (Part 3)
  • AI Meets Vector Databases: Redefining Data Retrieval in the Age of Intelligence
  1. DZone
  2. Coding
  3. Java
  4. Java Concurrency: The Happens-Before Guarantee

Java Concurrency: The Happens-Before Guarantee

In this article, learn more about reorderings and multi-threaded codebases along with how Java helps you with its guarantees.

By 
Emmanouil Gkatziouras user avatar
Emmanouil Gkatziouras
DZone Core CORE ·
Aug. 12, 24 · Tutorial
Likes (14)
Comment
Save
Tweet
Share
11.9K Views

Join the DZone community and get the full member experience.

Join For Free

Usually, when we write code, we have the assumption that the code is executed in the same sequence as it was written. This is not the case, since for optimization purposes, a re-ordering of the statements happens either on compile time or runtime.Java logo

Regardless when a thread runs a program, the result should be as if all of the actions occurred in the order they appear in the program. The execution of the single thread program should follow as-if-serial semantics. Optimizations and re-orderings can be introduced as long as the result is guaranteed to be the same as the results of the program should the statements have been executed sequentially.

Let’s see an example.

This block:

Java
 
var i = 0;
var j = 1;
j--;


Can be re-ordered to this block:

Java
 
var j = 1;
j--;
var i = 0;


We can add an extra allocation depending on the results of the previous blocks.

Java
 
var x = i+j;


Regardless of the re-orderings that occurred, the results should be as if each statement of the program was run sequentially.

From a single thread perspective, we are covered; however, when multiple threads operate on a block like this, there are various issues. The effects of a thread’s operations won’t be visible to the other thread in a predictable way.

Imagine the scenario where the execution of a code block by one thread is dependent on the results of the execution of another thread. This is the case of a happened-before relationship. We have two events, and the results should be the one as if one event happened before the other regardless of re-ordering.

Java has a happens-before guarantee.

Rules

We can check the documentation and see the rules that make the guarantee possible.

  1. An unlock on a monitor happens-before every subsequent lock on that monitor.
  2. A write to a volatile field happens-before every subsequent read of that field.
  3. A call to start() on a thread happens-before any actions in the started thread.
  4. All actions in a thread happen-before any other thread successfully returns from a join() on that thread.
  5. The default initialization of any object happens-before any other actions (other than default-writes) of a program.

They are self-explanatory. Let’s check some of their code.

1. An Unlock on a Monitor Happens-Before Every Subsequent Lock on That Monitor.

Every object in Java has an intrinsic lock. When we use synchronized, we use an object’s lock.

Supposing we have a class and some methods, and we use the object’s lock:

Java
 
public class HappensBeforeMonitor {
 
    private int x = 0;
    private int y = 0;
     
    public void doXY() {
        synchronized(this) {
            x = 1;
            y = 2;
        }
    }
 
 
}


Provided a thread calls doXY(). The lock that the object has cannot be unlocked before a lock has been acquired. The synchronized method, as we have seen previously, wraps the code contained with a lock and unlock statement. Any re-ordering optimization should not change the order of the lock operation and the unlock operation.

2. A Write to a volatile Field Happens-Before Every Subsequent Read of That Field.

Java
 
public class HappensBeforeVolatile {
 
    private volatile int amount;
 
 
    public void update(int newAmount) {
        amount =  newAmount;
    }
 
    public void printAmount() {
        System.out.println(amount);
    }
 
}


Assuming thread a calls update and then thread b calls to print the amount. The read will take place after the write. The write will write the value to the main memory. The result is thread b to have the value set from thread a.

3. A Call to start() on a Thread Happens-Before Any Actions in the Started Thread.

No re-ordering will affect the sequence between the actions on a thread and the action of a thread starting. All actions inside the thread will take place after the thread has started.

4. All Actions in a Thread Happen-Before Any Other Thread Successfully Returns From a join() on That Thread.

Thread b calls join on thread a. The operations inside thread a will take place before the join. When thread b's join call finishes, the changes in Thread a will be visible to thread b.

Java
 
private int x = 0;
private int y = 1;
 
public void calculate() throws InterruptedException {
...
    final Thread a = new Thread(() -> {
        y = x*y;
    });
    Thread b = new Thread(() -> {
 
        try {
            a.join();
            System.out.println(y);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });
 
    a.start();
    b.start();
    b.join();
...
}


5. The Default Initialization of Any Object Happens-Before Any Other Actions (Other Than Default-Writes) of a Program.

Take, for example, this plain class:

Java
 
public class HappensBeforeConstructor {
 
    private final  int x;
    private final  int y;
    public HappensBeforeConstructor(int a ,int b) {       
        x = a;
        y = b;
    }
 
}


If we think about it, the object instantiated inherits the Object.class just like every object in Java. If the extension of Object was not implicit, the class would be like this:

Java
 
public class HappensBeforeConstructor extends Object {
 
    private final  int x;
    private final  int y;
    public HappensBeforeConstructor(int a ,int b) {
        super();
        x = a;
        y = b;
    }
 
}


The super(); method instantiates the object. It’s the default initialization and no other operation in the constructor will be re-ordered and take place before it.

That’s all. In the next article, we will have a look at memory visibility.

Requirements engineering Blocks Java (programming language) Object (computer science) Data Types

Published at DZone with permission of Emmanouil Gkatziouras, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Redefining Java Object Equality
  • Singleton: 6 Ways To Write and Use in Java Programming
  • Creating a Deep vs. Shallow Copy of an Object in Java
  • Generics in Java and Their Implementation

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!