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

  • Mastering Thread-Local Variables in Java: Explanation and Issues
  • Unlocking Performance: Exploring Java 21 Virtual Threads [Video]
  • Visualizing Thread-Safe Singletons in Java
  • Double-Checked Locking Design Pattern in Java

Trending

  • MCP Servers: The Technical Debt That Is Coming
  • The Future of Java and AI: Coding in 2025
  • Scaling Microservices With Docker and Kubernetes on Production
  • Rust, WASM, and Edge: Next-Level Performance
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Java Patterns for Concurrency

Java Patterns for Concurrency

Want to learn more about Java patterns for concurrency? Check out this post to learn more about how to solve concurrency issues with multiple threads.

By 
Andy Gibson user avatar
Andy Gibson
·
Nov. 01, 18 · Tutorial
Likes (21)
Comment
Save
Tweet
Share
38.4K Views

Join the DZone community and get the full member experience.

Join For Free

This post talks about some of the patterns we use to solve concurrency issues relating to the state shared across multiple threads. The goal is to provide some smarter alternatives to slapping synchronized on every method call or around every block of code. The problem with synchronized is that it requires everyone to participate. If you have an object that is mutable and shared by synchronizing on it, then nearly every use of that object will require a synchronized block, and it only takes one person to forget to create bedlam. In general, the goal is to write code that we don’t need to consider concurrency, and we can write code without concerning ourselves with how everyone else it handling concurrency.

Limit State Changes To A Single Thread

This is a pattern that has been used in Swing and JMonkey Engine (JME) where changes to the common state should only be made in the main thread. This pattern is useful when you have tasks that go off and run, and once complete, they need to catch up and update the display or game objects in the case of JME. I put this one first as it is a very top level pattern. If you decide to go this route, then you don’t have to worry about concurrency in the project as long as you always follow the rule of updating state on the main thread.

In any component that runs asynchronously, once completed, it can schedule a piece of code to run on the main thread. Swing and JME offer their own ways of doing this, but you could create your own to allow something like the following:

public void execute() {
  int value = someLongProcessRunAsynchronously();
  MainThread.enqueue(() -> form.updateLabel(value+" records Found"));
}


The Runnable code will be pulled from the queue by the main thread and executed. Obviously, any code run off the queue must be brief and not trigger any long-running synchronous actions.

Duplicate State to Make Is Local

We can duplicate mutable state into a local variable and reference it locally to take advantage that locally scoped data is inherently thread-safe.

//don't move or update if -ve
if (sharedInt >= 0) {
  moveBy(sharedInt);
  update();
}


The problem with this code is that if another thread interrupts it mid-execution and sets the shared int value to a negative number, it will have undesired results.

If we copy the value and then work only with our local copy, then we are guaranteed to have the same version each time it is referenced in our function.

//don't move or update if -ve
int localInt = sharedInt;
if (localInt >= 0) {
  moveBy(localInt);
  update();
}


Here, we have localized the state so it cannot be touched by other threads, but the downside of this is that it can be tricky with more complex objects since we must copy the object atomically. Even working with the built-in collections can be tricky. For this reason, we might need the help of some other patterns to ensure that can happen.

Encapsulate State

If we have a map of key-value pairs, we will probably expose the map to the world and let our threads have at it.

public class MyState {

  private Map<String,String> parameters;

  public Map<String,String> getParameters() {
    return parameters;
  }
}


//client code 


   myState.getParameters().put("key","value");
   if (myState.getParameters().contains("this")) {
     //assume 'that' is also present
     callFunction(myState.getParameters().get("that");
   }


This opens a host of problems since any thread can get the map, keep references to it, and update it any time. This is the least thread-safe thing we can do as our state has escaped.

First off, let's realize that just because we use a map to store some data, we don’t actually need to provide all access to the map to clients. In the interest of making it thread safe, we can encapsulate the map and just provide methods to access that map. Our state has no longer leaked out of the class and is somewhat protected. Once encapsulated, we can control how we access the map and do so in a manner that is more thread-safe.

For a first draft, we can simply make access methods synchronized :

public class MyState {

  private Map<String,String> parameters;

  public synchronized void addParameter(String key, String value) {

    parameters.put(key,value);
  }

  public synchronized String getParameter(String key) {
    return parameters.get(key);
  }
}


We’ve made our code safer and, arguably, a little better according to the Law of Demeter.

One problem is that we have put synchronized on the method, and if we have other synchronized methods in the same class, even for different reasons, they will end up blocking each other unnecessarily. One solution is just to synchronize on the parameters object when we use it, which should improve things a little, but there are some alternatives we can use to make things even better. Let's take a closer look:

Read/Write Locks

If we read from an object far more than we write, we could improve the above example further. We can use read/write locks to ensure that we block read access to the object when we are writing, but we can concurrently read from the object without being blocked by other readers. Readers will, however, be blocked by writers, which should be infrequent.

public class MyState {

  private Map<String,String> parameters;
  private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

  public void addParameter(String key, String value) {
    lock.writeLock().lock();
    try { 
      parameters.put(key,value);
    } finally {
      lock.writeLock().unlock();
    }
  }

  public String getParameter(String key) {
    lock.readLock().lock();
    try { 
      return parameters.get(key);
    } finally {
      lock.readLock().unlock();
    }
  }
}


This requires more code, but it allows nearly unlimited concurrent reads with only writes blocking. We also have some options for dealing with cloning state atomically, which was an issue raised above. (As an aside, why do we not have a method for passing the lockable code as a lambda such as lock.readLock().execute(()->parameters.get(key));?).

We have some nice options with locks that don’t make much sense here, but we can have timeouts for locks, etc., and as far as the interface goes, we can implement whatever helper functions we want and use the locks to handle them easily, for example, both get and set functions.

As an example, we can easily make a copy of the map without worrying about a write action disrupting things mid-execution:

public Map<String,String> getParameters() {
  lock.readLock().lock();
  try { 
    return new HashMap<>(parameters);
  } finally {
    lock.readLock().unlock();
  }
}


This means that we can handle making a local copy of the state atomically and use it in conjunction with the local duplicate state pattern. This provides an implementation that is thread-safe without the client having to consider thread safety or use it in a thread-unsafe manner.

Summary

These are some of the patterns you can use to defuse any concurrency issues with a mutable state without having to resort to putting synchronization on everything. Happy coding!

Threading Java (programming language)

Published at DZone with permission of Andy Gibson, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Mastering Thread-Local Variables in Java: Explanation and Issues
  • Unlocking Performance: Exploring Java 21 Virtual Threads [Video]
  • Visualizing Thread-Safe Singletons in Java
  • Double-Checked Locking Design Pattern in Java

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!