Jenkov.com
Tutorials Books About
Java Concurrency

1 Java Concurrency / Multithreading Tutorial
2 Multithreading Benefits
3 Multithreading Costs
4 Creating and Starting Java Threads
5 Race Conditions and Critical Sections
6 Thread Safety and Shared Resources
7 Thread Safety and Immutability
8 Java Synchronized Blocks
9 Thread Signaling
10 Deadlock
11 Deadlock Prevention
12 Starvation and Fairness
13 Nested Monitor Lockout
14 Slipped Conditions
15 Locks in Java
16 Read / Write Locks in Java
17 Reentrance Lockout
18 Sempahores
19 Blocking Queues
20 Thread Pools
21 Anatomy of a Synchronizer




Race Conditions and Critical Sections


Running more than one thread inside the same application does not by itself cause problems. The problems arise when multiple threads access the same resources. For instance the same memory (variables, arrays, or objects), systems (databases, web services etc.) or files. In fact, problems only arise if one or more of the threads write to these resources. It is safe to let multiple threads read the same resources, as long as the resources do not change.

Here is a code example that may fail if executed by multiple threads simultaneously:

  public class Counter {

     protected long count = 0;

     public void add(long value){
         this.count = this.count + value;
     }
  }

Imagine if two threads, A and B, are executing the add method on the same instance of the Counter class. There is no way to know when the operating system switches between the two threads. The code is not executed as a single instruction by the Java virtual machine. Rather it is executed along the lines of:

   get this.count from memory into register
   add value to register
   write register to memory

Observe what happens with the following mixed execution of threads A and B:

       this.count = 0;
   A:  reads this.count into a register (0)
   B:  reads this.count into a register (0)
   B:  adds value 2 to register
   B:  writes register value (2) back to memory. this.count now equals 2
   A:  adds value 3 to register
   A:  writes register value (3) back to memory. this.count now equals 3

The two threads added the values 2 and 3 to the counter. Thus the value should have been 5 after the two threads complete execution. However, since the execution of the two threads is interleaved, both threads read the value 0 from memory. Then they add their individual values, 2 and 3, to the value, and write the result back to memory. Instead of 5, the value left in this.count will be the value written by the last thread to write its value. In the above case it is thread A, but it could as well have been thread B. Without proper thread synchronization mechanisms there is no way to know exactly how the thread execution is interleaved.

Race Conditions & Critical Sections

The situation where two threads compete for the same resource, where the sequence in which the resource is accessed is significant, is called race conditions. A code section that leads to race conditions is called a critical section. In the previous example the method add() is a critical section, leading to race conditions. Race conditions can be avoided by proper thread synchronization in critical sections.



Connect with me: Newsletter - Get all my free tips!