24

If multiple threads try to update the same member variable, it is called a race condition. But I was more interested in knowing how the JVM handles it internally if we don't handle it in our code by making it synchronised or something else? Will it hang my program? How will the JVM react to it? I thought the JVM would temporarily create a sync block for this situation, but I'm not sure what exactly would be happening.

If any of you have some insight, it would be good to know.

senshin
  • 10,022
  • 7
  • 46
  • 59
krmanish007
  • 6,749
  • 16
  • 58
  • 100
  • 4
    This is an awesome question because Java is one of the few "interpreted" languages with a VM which has real threading support. Java threads are truly executing with native threads, where languages like Python have fake threading support (I'm looking at you, GIL). – vz0 Nov 28 '14 at 16:31
  • I found some discussion [here](http://programmers.stackexchange.com/questions/262428/race-conditions-in-jvm-languages-versus-c-c), waiting to see what SO has to say in depth – mprabhat Nov 28 '14 at 16:35
  • it doesn't. whichever thread is running first (which depends on a lot of parameters, including external ones) wins the race. The outcome is typically not predictable. The JVM itself is not even aware that there is a data race. – njzk2 Nov 28 '14 at 16:40
  • Glance also at clojure's transactional memory module that doesn't have race conditions. –  Nov 28 '14 at 16:44
  • JVM does NOT handle race conditions anyhow. If you have a race condition in your code, then your program has a bug and you have to fix it by yourself. The JVM doesn't automagically fix that for you. – Natix Nov 28 '14 at 17:01
  • I don't want JVM to fix it but want to know how JVM will react. What possible outcome would be! – krmanish007 Nov 28 '14 at 17:03
  • Don't desperate with these poor people doesn't even understand your question, this is the java world. The sun jvm was written in c++, and on every system it uses the thread locking mechanisms of the OS, although there is a significant imprevement in the latest jvms to minimalize the cases if there is even a chance of a race problem. – peterh Nov 28 '14 at 21:21

5 Answers5

17

The precise term is a data race, which is a specialization of the general concept of a race condition. The term data race is an official, precisely specified concept, which means that it arises from a formal analysis of the code.

The only way to get the real picture is to go and study the Memory Model chapter of the Java Language Specification, but this is a simplified view: whenever you have a data race, there is almost no guarantee as to the outcome and a reading thread may see any value which has ever been written to the variable. Therein also lies the only guarantee: the thread will not observe an "out-of-thin-air" value, such which was never written. Well, unless you're dealing with longs or doubles, then you may see torn writes.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • I tried to find an exact anwser Marko in the java lang specification, but haven't able to reach to the exact answer. Outcome is not guarenteed is totally fine, but what I don't want is some unfriendly behaviour like system is not responding as none of the thread is getting an access to update it!! – krmanish007 Nov 28 '14 at 16:45
  • 2
    Are you talking about deadlocks? Without locking (`synchronized` blocks, for example) they cannot possibly arise. – Marko Topolnik Nov 28 '14 at 16:51
  • Definitely it won't be a dead lock as there is no cycle of wait, but its just that JVM doesn't know what to do as JVM is having two request at a same time! I think there needs to be a wait for another to finish before allowing other thread to update. – krmanish007 Nov 28 '14 at 16:57
  • 1
    No, they can both update the same value at the same time. Note that this assumes the two threads are running each on its own core. Each core has its own first-level cache and can update the value there. In the case of Intel CPUs, it will guarantee that there is a definite order of writes as observed by other threads/cores. The JVM doesn't have to deal with that. Other CPUs (such as ARM) give less guarantees, but it is still quite easy to comply with the Java Memory Model. – Marko Topolnik Nov 28 '14 at 17:06
  • Thats a good answer, But what happen if it is a volatile variable as in that case it won't be stored in CPU first level cache but will be accessed directly from heap? – krmanish007 Nov 28 '14 at 17:08
  • 1
    If you have a volatile variable, then you don't have a data race, and your question is about that. When you say "heap", you probably mean the main DRAM memory banks. And no, on Intel you don't have to access main DRAM to stay compliant with `volatile` because Intel guarantees CPU cache coherency. On other CPUs you'll just need to emit an appropriate *memory barrier* instruction, which will then deal with coherency. If you're interested in those details, may I refer you here: http://gee.cs.oswego.edu/dl/jmm/cookbook.html – Marko Topolnik Nov 28 '14 at 17:11
  • I am not very much sure about it but will have a read. Its good to know. – krmanish007 Nov 28 '14 at 17:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/65861/discussion-between-krmanish007-and-marko-topolnik). – krmanish007 Nov 28 '14 at 23:30
  • Will there be torn writes if you're on a 64-bit machine with a 64-bit JVM? – Octavia Togami Nov 29 '14 at 06:18
  • I would recommend stepping away from the direction these comments are going. If you are doing multithreaded applications, it is worth taking the time to learn how to do them properly, with proper synchronization. Learning clever hacks to get around threading properly by abusing the underlying VM opcodes and the underlying machine level bytecodes just makes it harder to learn to do it correctly in the future. – Cort Ammon Nov 29 '14 at 07:13
  • @ıɯɐƃoʇǝızuǝʞ The specification does not guarantee the atomicity of `long` and `double` writes, that's it. Whether you can ever observe them on a given JVM implementation is beside the point in this context. – Marko Topolnik Nov 29 '14 at 09:59
  • @ıɯɐƃoʇ ǝızuǝʞ: in principle, the answer is *yes*, it is possible to encounter torn writes on a 64 Bit JVM as there is no guaranty that a 64 Bit JVM uses 64 Bit Updates all the time. E.g. it’s optimizer still might read/write a 32 Bit half value if it knows that the other half has not been modified (by that thread). This might also apply when utilizing SIMD extensions like SSE which can process more items in one step when using smaller data sizes. It’s still unlikely that you ever *see* such a torn update and there’s no example code to exhibit it, but you can’t rely on atomicity for 64 Bit. – Holger Dec 01 '14 at 13:28
5

Maybe I'm missing something but what is there to handle? There is still a thread that will get there first. Depending on which thread that is, that thread will just update/read some variable and proceed to the next instruction. It can't magically construct a sync block, it doesn't really know what you want to do. So in other words what happens will depend on the outcome of the 'race'.

Note I'm not heavily into the lower level stuff so perhaps I don't fully understand the depth of your question.

Sebastiaan van den Broek
  • 5,818
  • 7
  • 40
  • 73
  • If there is some time difference its fine, but if JVM is already in the process of updating the member object and before it finishes another thread is trying to update it again. This can happen in the current multi threaded model. Ideally JVM should wait for first to finish but then that is temp sync block! – krmanish007 Nov 28 '14 at 16:40
  • `in the process of updating the member object`. That's the whole point. Some operations are atomic (`i = 1`), all the others can be disrupted by another thread, but the JVM has no way of knowing that this is not the effect intended by your code. (also, that is what `synchronized` is for) – njzk2 Nov 28 '14 at 16:42
  • Thanks, looking forward to other answers to this question, but I like mprabhat's link in the original question's comments. – Sebastiaan van den Broek Nov 28 '14 at 16:49
2

Java provides synchronized and volatile to deal with these situations. Using them properly can be frustratingly difficult, but keep in mind that Java is only exposing the complexity of modern CPU and memory architectures. The alternatives would be to always err on the side of caution, effectively synchronizing everything which would kill performance; or ignore the problem and offer no thread safety whatsoever. And fortunately, Java provides excellent high-level constructs in the java.util.concurrent package, so you can often avoid dealing with the low-level stuff.

Kevin Krumwiede
  • 9,868
  • 4
  • 34
  • 82
  • I don't think this is really an answer to the question. It's about the effect when you DON'T use synchronization concepts yourself. – Sebastiaan van den Broek Nov 28 '14 at 16:50
  • Yes Kevin, its always good to use concurrentHashMap or something similar to avoid these situation, which is the most optimum solution in this situation but I was more interested in knowing what happen if we use the basic collection API! – krmanish007 Nov 28 '14 at 16:51
1

In short, the JVM assumes that code is free of data races when translating it into machine code. That is, if code is not correctly synchronized, the Java Language Specification provides only limited guarantees about the behavior of that code.

Most modern hardware likewise assumes that code is free of data races when executing it. That is, if code is not correctly synchronized, the hardware makes only limited guarantees about the result of its execution.

In particular, the Java Language Specification guarantees the following only in the absence of a data race:

  • visibility: reading a field yields the value last assigned to it (it is unclear which write was last, and writes of long or double variables need not be atomic)
  • ordering: if a write is visible, so are any writes preceding it. For instance, if one thread executes:

    x = new FancyObject();
    

    another thread can read x only after the constructor of FancyObject has executed completely.

In the presence of a data race, these guarantees are null and void. It is possible for a reading thread to never see a write. It is also possible to see the write of x, without seeing the effect of the constructor that logically preceded the write of x. It is very unlikely that the program is correct if such basic assumptions can not be made.

A data race will however not compromise the integrity of the Java Virtual Machine. In particular, the JVM will not crash or halt, and still guarantee memory safety (i.e. prevent memory corruption) and certain semantics of final fields.

meriton
  • 68,356
  • 14
  • 108
  • 175
0

The JVM will handle the situation just fine (ie it will not hang or complain), but you may not get a result that you like!

When multiple threads are involved, java becomes fiendishly complicated and even code that looks obviously correct can turn out to be horribly broken. As an example:

public class IntCounter {
    private int i;

    public IntCounter(int i){
         this.i = i;
    }

    public void incrementInt(){
        i++;
    }

    public int getInt(){
        return i;
    }
}

is flawed in many ways.

First, let's say that i is currently 0 and thread A and thread B both call incrementInt() at about the same time. There is a danger that they will both see that i is 0, then both increment it 1 and then save the result. So at the end of the two calls, i is only 1, not 2!

That's the race condition problem with the code, but there are other problems concerning memory visibility. When thread A changes a shared variable, there is no guarantee (without synchronization) that thread B will ever see the changes!

So thread A could increment i 100 times, and an hour later, thread B, calling getInt(), might see i as 0, or 100 or anywhere in between!

The only sane thing to do if you are delving into java concurrency is to read Java Concurrency in Practice by Brian Goetz et al. (OK there's probably other good ways to learn about it, but this is a great book co written by Joshua Bloch, Doug Lea and others)

user384842
  • 1,946
  • 3
  • 17
  • 24
  • 1
    Oh my God, you are around the 10th person doesn't even understood this question... – peterh Nov 28 '14 at 21:23
  • OP's question was "Will it hang my program, how JVM will react it?" I answered "it will not hang or complain," which appears to me to answer his question satisfactorily. Since his question suggested that he was unfamiliar with java concurrency as a whole, I gave him some additional information that I thought might help him avoid egregious errors. I like to try to be constructive. I'm interested why you think that I didn't understand his question, or what you think his question is. – user384842 Nov 28 '14 at 21:54
  • @krmanish007 There is no JVM. There is only the Java Language Specification (JLS). _A_ JVM (not _the_ JVM) is one component of the run-time portion of most implementations of the the JLS. If any particular JVM fails to do what the JLS says it should do, then it's the JVM that is wrong. The JLS is the authority. The JLS is not an easy read, but it _does_ say what may happen and what may not happen when two different threads access the same variable with no synchronization. – Solomon Slow Nov 30 '14 at 21:30