Java avoid race condition WITHOUT synchronized/lock

17,755

Solution 1

Atomics are indeed more efficient than classic locks due to their non-blocking behavior i.e. a thread waiting to access the memory location will not be context switched, which saves a lot of time.

Probably the best guideline when synchronization is needed is to see how you can reduce the critical section size as much as possible. General ideas include:

  1. Use read-write locks instead of full locks when only a part of the threads need to write.
  2. Find ways to restructure code in order to reduce the size of critical sections.
  3. Use atomics when updating a single variable.
  4. Note that some algorithms and data structures that traditionally need locks have lock-free versions (they are more complicated however).

Solution 2

Avoid state.
Make your application as stateless as it is possible.
Each thread (sequence of actions) should take a context in the beginning and use this context passing it from method to method as a parameter.

When this technique does not solve all your problems, use the Event-Driven mechanism (+Messaging Queue).
When your code has to share something with other components it throws event (message) to some kind of bus (topic, queue, whatever).

Components can register listeners to listen for events and react appropriately.
In this case there are no race conditions (except inserting events to the queue). If you are using ready-to-use queue and not coding it yourself it should be efficient enough.

Also, take a look at the Actors model.

Solution 3

Well, first off Atomic classes uses locking (via synchronized and volatile keywords) just as you'd do if you did it yourself by hand.

Second, immutability works great for multi-threading, you no longer need monitor locks and such, but that's because you can only read your immutables, you cand modify them.

You can't get rid of synchronized/volatile if you want to avoid race conditions in a multithreaded Java program (i.e. if the multiple threads cand read AND WRITE the same data). Your best bet is, if you want better performance, to avoid at least some of the built in thread safe classes which do sort of a more generic locking, and make your own implementation which is more tied to your context and thus might allow you to use more granullar synchronization & lock aquisition.

Check out this implementation of BlockingCache done by the Ehcache guys;

http://www.massapi.com/source/ehcache-2.4.3/src/net/sf/ehcache/constructs/blocking/BlockingCache.java.html

Solution 4

One of the alternatives is to make shared objects immutable. Check out this post for more details.

Solution 5

You can perform up to 50 million lock/unlocks per second. If you want this to be more efficient I suggest using more course grain locking. i.e. don't lock every little thing, but have locks for larger objects. Once you have much more locks than threads, you are less likely to have contention and having more locks may just add overhead.

Share:
17,755
cn1h
Author by

cn1h

Updated on June 05, 2022

Comments

  • cn1h
    cn1h almost 2 years

    In order to avoid race condition, we can synchronize the write and access methods on the shared variables, to lock these variables to other threads.

    My question is if there are other (better) ways to avoid race condition? Lock make the program slow.

    What I found are:

    • using Atomic classes, if there is only one shared variable.
    • using a immutable container for multi shared variables and declare this container object with volatile. (I found this method from book "Java Concurrency in Practice")

    I'm not sure if they perform faster than syncnronized way, is there any other better methods?

    thanks

  • Shivan Dragon
    Shivan Dragon over 12 years
    I don't think replacing Java's internal monitor locking with external queues/even-driven-mechanisms will improve on performance. Making the application completly stateless might not be an option here either, as the asker already states that it has data to share between the threads. The Actor model might be useful as far as code cleanliness is concerned but again, I think the added overhead will not benefit towards performance.
  • AlexR
    AlexR over 12 years
    @Andrei Bodnarescu, I absolutely agree with you. I probably expressed myself not exactly. Performance here is not the main issue. Code clearness, modularity and managebility are much more important. I just wanted to say that using event drive approach will not decrease performance as some people afraid.
  • Shivan Dragon
    Shivan Dragon over 12 years
    Agreed, and if I may add, Scala's Actors model implementation is quite nice. And since Scala is one step away from Java for a Java developer, that might be an option.
  • Boris
    Boris over 12 years
    Atomic-classes don't (generally) use synchronized. Their values are volatile, that's correct. Instead of synchronized the sun.misc.Unsafe-Class is used which does CAS-operations as native code. So if the platform your running on supports atomic CAS-operations in assembler, its most likely that the are used instead of synchronizing.
  • cn1h
    cn1h over 12 years
    "3. Use atomics when updating a single variable." If there are more variables, but they are independent (nerver be used in same function), can I use atomics as well?
  • Tudor
    Tudor over 12 years
    Yes, of course. But you will need to use atomics for each of them.
  • cn1h
    cn1h over 12 years
    about "make the application stateless", if I don't have shared data between threads, but i have something shared between functions in the same thread, I should use ThreadLocal class, right?