1

Consider the following code :

class Abs
{
    int x = 0;
    public void increment ( )
    {
        x++;
        System.out.println( x );
    }
}
class Task1 extends Thread
{
    Abs t;
    public Task1 ( Abs x ) { t = x; }
    public void run ( ) { t.increment(); }
}
public class Multi
{
    public static void main ( String args[] )
    {
        Abs obj = new Abs();
        Task1 t1 = new Task1( obj );
        Task1 t2 = new Task1( obj );

        t1.start();
        t2.start();
    }
}

This will make the member variable x of object obj shared between the threads t1 and t2, and hence, race condition can occur resulting in the following output:

    2
    2

This is because the object obj is in the heap, and hence, it is shared between the threads t1 and t2 ( and consequently sharing the member variable x).

What I am confused about is that every thread will have an instance member t of class Abs. Now in the constructor we are assigning this instance member to the passed Abs object ( t=x; ). What confuses me is that since we have an instance member for every thread, this means that this instance member will be in the stack of every thread and when we assign this instance member to the passed Abs object, this means that every thread will have its own Abs object as they will be in the stack of the thread (because the instance member is in the stack of the thread), and therefore, the object obj will not be shared between the two threads t1 and t2. So how the object obj is actually shared between the two threads in this example?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
AAA
  • 305
  • 1
  • 7
  • 2
    what is your question about? you should have a look at how to ask a well-received question here. – catch23 Apr 09 '23 at 21:13
  • 3
    `What I am confused about is that every thread will have an instance member t` No. Each thread has a *reference* to the object. A reference is like a pointer. There's only one heap memory object of type `Abs` allocated, and the `t` "object" definitely does not appear on any thread's stack. – markspace Apr 09 '23 at 21:14
  • @catch23 Sorry I forgot to include the question. Now I have edited it. Thanks. – AAA Apr 09 '23 at 21:17
  • @markspace So this means that Abs t; is just a refence to the object that will be passed to the constructor and not a variable in the stack of the thread ? If yes , so this means that if we have declared another member variable int x; , this means that this is also a reference to an integer and it is not a variable field in the stack of the thread ? Kindly clarify . – AAA Apr 09 '23 at 21:21
  • 1
    No, primitives like `int` are values, not references. The value appears directly as a instance field, not a reference to a value on the heap. – markspace Apr 09 '23 at 21:22
  • 1
    There is only "one instance" of Abs obj created, both classes t1 and t2 have been loaded with the same instance.. It is bad textbook writing, it is not a.k.a shared, simply both have access to it!(and it may ever have better functionality in that situation designed as a static class but you are probably not ready for that problem concept , Worse, "sharing" in that situation can easily require "asynchronous" access handling, and that may be as heavy a concept as static with reference to "shared") The real fun starts at "volatile" keyword. – Samuel Marchant Apr 09 '23 at 22:54
  • 1
    [Thread Interference](https://docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html) – MadProgrammer Apr 09 '23 at 23:04
  • 2
    Bruh, you are using the **exact same instance** in both threads. The fact that reference to that object is duplicated (or not, i dont event know), and is (or not) allocated on each thread heap does not matter as you will still use the same object a. Its like writing your home address on multiple sheets of paper and expecting that your house will magically clone itself so you can have N houses now:) – Antoniossss Apr 10 '23 at 00:14

1 Answers1

2

One object, two references

You said:

every thread will have an instance member t of class Abs

we have an instance member for every thread

No, not so.

Each of your two Task1 objects holds a reference to an object of type Abs, the very same single object. You instantiated only a single Abs object. Then you passed a reference to each of the new Task1( obj ) calls.

Knowing the difference between an object (a chunk of memory) and a reference (basically, a pointer to that chunk’s location in memory) is crucial here. If you are not clear on this, study the subject.

Since you have only one Abs object in your example app, there is only one int primitive variable named x. So your code is dealing with a single number value. Each of your threads is accessing, and replacing, that value. So your code is not thread-safe.

See also comment by markspace.

Abs obj = new Abs();          // (1) Create one single object. (2) Create a reference to that object. (3) Store that reference in a variable named `obj`.
Task1 t1 = new Task1( obj );  // Pass a *reference* to this constructor.
Task1 t2 = new Task1( obj );  // Pass a *reference* to this constructor. Happens to be the very same reference passed in line above.
// At this point, both `t1` and `t2` hold a reference leading to the same single `Abs` object. One object in memory, two references to that object.

screenshot of two Task1 objects, but only a single Abs object

Race condition

The race condition occurs because x++ is not an atomic operation, as discussed here. Three separate operations occur:

  • Reading x.
  • Incrementing for a new value.
  • Writing the new value back to x.

Being non-atomic means one thread can read the value of that number, and in an intervening moment, a second thread can both read and increment that same number, writing a new value to the x variable. A moment later, the first thread increments its previously read value, and stores that new value back into x. (Which threads operate in what order for however amount of time between pauses is completely unpredictable!)

So both threads can read a value of 1. And both threads can increment that value to 2. The value 2 can be written twice, once by each of the two threads.

The lesson to learn in concurrency is that any mutable resource shared across threads must be protected. (Simple in concept, tricky in practice.)

AtomicInteger

In your case, one simple way to add that protection is to change your int x primitive to an AtomicInteger object. The AtomicInteger class makes reading, writing, and reporting an integer value atomic.

In Abs class, change the declaration of x. And change the incrementing line x++ to call a method on AtomicInteger instead.

We mark x as final to prevent any reassignment to a different AtomicInteger object. Reassignment would ruin the integrity of our purpose of a rolling counter.

class Abs
{
    final AtomicInteger x = new AtomicInteger( 0 ) ;
    public void increment ( )
    {
        int newValue = x.incrementAndGet() ;
        System.out.println( newValue );
    }
}

A fine point on code above: We instantiate our AtomicInteger as part of the declaration because this guarantees per the Java specification that the code will be run before any other code has a chance to access this member field. Combined with final, we know that all threads will see and access the same single reference stored in x in a thread-safe manner.


By the way, in modern Java we rarely address the Thread class directly. Generally best to use the Executors framework instead.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Well described; especially from the section, **Race condition** onwards! The mention of the Executors framework makes the answer even more valuable. – Arvind Kumar Avinash Apr 12 '23 at 13:04