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

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Selenium Grid Tutorial: Essential Tips and How to Set It Up
  • Automating Cucumber Data Table to Java Object Mapping in Your Cucumber Tests
  • JUnit 5 Custom TestListeners
  • Singleton: 6 Ways To Write and Use in Java Programming

Trending

  • Navigating Double and Triple Extortion Tactics
  • Unlocking the Potential of Apache Iceberg: A Comprehensive Analysis
  • Comparing Managed Postgres Options on The Azure Marketplace
  • Mastering Deployment Strategies: Navigating the Path to Seamless Software Releases
  1. DZone
  2. Coding
  3. Java
  4. How to Test If a Class Is Thread-Safe in Java

How to Test If a Class Is Thread-Safe in Java

Learn how to test if a class is thread-safe in Java.

By 
Thomas Krieger user avatar
Thomas Krieger
·
Dec. 16, 19 · Tutorial
Likes (9)
Comment
Save
Tweet
Share
56.2K Views

Join the DZone community and get the full member experience.

Join For Free

Learn how to test if a class is thread-safe in Java.


Tests for thread safety differ from typical single-threaded tests. To test if a method is thread-safe we need to call the method in parallel from multiple threads. We need to do this for all potential thread interleavings. And afterward, we need to check if the result is correct.

Those three requirements for our test lead to a special type of tests for thread safety which differ from typical single-threaded tests. Since we want to test all thread interleavings our test must be repeatable and run automatically. And since the methods run in parallel the potential result is a combination of different outcomes.


You may also like:  What Does Thread-Safety Mean in Java?

Let us look at an example to see how this looks in practice.

Testing for Thread Safety

Suppose we want to test if the following class representing an Address is thread-safe. It offers one method to update the street and city, the method update and one method to read the complete Address, the method  toString:

Java
 




x
19


 
1
public class MutableAddress {
2
    private volatile String street;
3
    private volatile String city;
4
    private volatile String phoneNumber;
5
    public MutableAddress(String street, String city, 
6
        String phoneNumber) {
7
        this.street = street;
8
        this.city = city;
9
        this.phoneNumber = phoneNumber;
10
    }
11
    public void update(String street ,String city ) {
12
        this.street = street;
13
        this.city = city;
14
    }
15
    public String toString() {
16
        return "street=" + street + ",city=" + city + ",
17
        phoneNumber=" + phoneNumber;
18
    }
19
}
6
        String phoneNumber) {



I use volatile fields, line 2 through 4, to make sure that the threads always see the current values, as explained in greater detail here. You can download the source code of all examples from GitHub here.

Now, let us first see if the combination of  toString and update is thread-safe. Here is the test:

Java
 




xxxxxxxxxx
1
24


 
1
import com.vmlens.api.AllInterleavings;
2
public class TestToStringAndUpdate {
3
    @Test
4
    public void testMutableAddress() throws InterruptedException {
5
        try (AllInterleavings allInterleavings = 
6
            new AllInterleavings("TestToStringAndUpdate_Not_Thread_Safe");) {
7
            while (allInterleavings.hasNext()) {
8
                MutableAddress address = new MutableAddress("E. Bonanza St.",
9
                     "South Park", "456 77 99");
10
                String readAddress = null;
11
                Thread first = new Thread(() -> {
12
                    address.update("Evergreen Terrace", "Springfield");
13
                });
14
                first.start();
15
                readAddress = address.toString();
16
                first.join();
17
                assertTrue("readAddress:" + readAddress,readAddress.equals(
18
        "street=E. Bonanza St.,city=South Park,phoneNumber=456 77 99") 
19
                    || readAddress.equals(
20
        "street=Evergreen Terrace,city=Springfield,phoneNumber=456 77 99"));
21
            }
22
        }
23
    }
24
}



The test executes the two methods in parallel from two threads. To test all thread interleavings, we put the complete test in a while loop iterating over all thread interleavings using the class AllInterleavings from vmlens, line 7. To see if the class is thread-safe, we compare the result against the to potential outcomes, the value before the update and after the update, lines 17 through 20.

Running the test leads to the following error:

Java
 




xxxxxxxxxx
1


 
1
java.lang.AssertionError: readAddress:street=Evergreen Terrace
2
        ,city=South Park,phoneNumber=456 77 99
3
    at com.vmlens.tutorialCopyOnWrite.TestToStringAndUpdate.
4
        testMutableAddress(TestToStringAndUpdate.java:22)



To see what went wrong, we look at the report vmlens generated:

The problem is that for one thread interleaving the thread with Thread id 30 first updates the street name and then the main thread, thread id 1, reads the street and city name. So, the main thread reads a partial updated address which leads to the error.

To make the address class thread-safe, we copy the address value every time we update the address. Here is a thread-safe implementation using this technique. It consists of two classes, an immutable value, and a mutable container.

First, the immutable value class:

Java
 




xxxxxxxxxx
1
21


 
1
public class AddressValue {
2
    private final String street;
3
    private final String city;
4
    private final String phoneNumber;
5
    public AddressValue(String street, String city, 
6
                String phoneNumber) {
7
        super();
8
        this.street = street;
9
        this.city = city;
10
        this.phoneNumber = phoneNumber;
11
    }
12
    public String getStreet() {
13
        return street;
14
    }
15
    public String getCity() {
16
        return city;
17
    }
18
    public String getPhoneNumber() {
19
        return phoneNumber;
20
    }
21
}



Second is the mutable container class:

Java
 




xxxxxxxxxx
1
21


 
1
public class AddressUsingCopyOnWrite {
2
    private volatile AddressValue addressValue;
3
    private final Object LOCK = new Object();
4
    public AddressUsingCopyOnWrite(String street, 
5
            String city, String phone) {
6
        this.addressValue = new AddressValue( street, 
7
                city,  phone);
8
    }
9
    public void update(String street ,String city ) {
10
        synchronized(LOCK){
11
            addressValue = new AddressValue(  street,  city,  
12
                    addressValue.getPhoneNumber() );
13
        }
14
    }
15
    public String toString() {
16
        AddressValue local = addressValue;
17
        return "street=" + local.getStreet()
18
        + ",city=" +    local.getCity() + 
19
        ",phoneNumber=" + local.getPhoneNumber();
20
    }
21
}



The class  AddressUsingCopyOnWrite creates a new address value every time it updates the variable  addressValue. This makes sure that we always read a consistent address, either the value before or after the update.

If we run the test with those two classes, the test succeeds.

What Do We Need to Test?

So far, we tested the combination of toString and  update for thread safety. To test if a class is thread-safe, we need to test all combinations of modifying methods and all combinations of read-only methods together with modifying methods. So, for our example class, we need to test the following two combinations:

  1.  update and update 
  2. toString and update 

Since the combinations of read-only methods are automatically thread-safe, we do not need to test the combination of the method toString with itself.

Data Races

So far, we used volatile fields to avoid data races. Let us see what happens when we use normal fields instead. So, in our thread-safe class  AddressUsingCopyOnWrite, we remove the volatile modifier and re-run our test. Now, vmlens reports a data race in the file target/interleave/issues.html

A data race is an access to a field where a thread might read a stale value. If the thread, indeed, reads a stale value depends on external factors like which optimizations the compiler is using or on which hardware architecture the JVM is running and on which cores the threads are running. To make it possible to always detect such a data race independent of those external factors, vmlens searches for data races in the execution trace of the test run. And if vmlens have found one as in the example, it reports them in the issue report.

Summary

Tests for thread safety differ from typical single-threaded tests. To test if the combination of two methods, a and b, is thread-safe, call them from two different threads. Put the complete test in a while loop iterating over all thread interleavings with the help from the class AllInterleavings from vmlens. Test if the result is either an after b or b after a. And to test if a class is a thread-safe, test all combinations of modifying methods and all combinations of read-only methods together with modifying methods.

Further Reading

7 Techniques for Thread-Safe Classes

What Does Thread-Safety Mean in Java?

5 Tips to Make Your Classes Thread-Safe

Testing Java (programming language) Thread safety

Published at DZone with permission of Thomas Krieger, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Selenium Grid Tutorial: Essential Tips and How to Set It Up
  • Automating Cucumber Data Table to Java Object Mapping in Your Cucumber Tests
  • JUnit 5 Custom TestListeners
  • Singleton: 6 Ways To Write and Use in Java Programming

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!