1

Consider the following simple example:

public class Example extends Thread {
    private int internalNum;

    public void getNum() {
        if (internalNum > 1)
            System.out.println(internalNum);
        else 
            System.out.println(1000);
    }

    public synchronized modifyNum() {
        internalNum += 1;
    }

    public void run() {
        // Some code
    }
}

Let's say code execution is split in two threads. Hypothetically, following sequence of events occurs:

  • First thread accesses the getNum method and caches the internalNum which is 0 at the moment.
  • At the very same time second thread accesses modifyNum method acquiring the lock, changes the internalNum to 1 and exits releasing the lock.
  • Now, first thread continues it execution and prints the internalNum.

The question is what will get printed on the console?

My guess is that this hypothetical example will result in 1000 being printed on the console because read and write flushes are only forced on a particular thread when entering or leaving the synchronized block. Therefore, first thread will happily use it's cached value, not knowing it was changed.

I am aware that making internalNum volatile would solve the possible issue, however I am only wondering weather it is really necessary.

Marin Veršić
  • 404
  • 4
  • 10
  • There is no concept of cache and flush in the Java Memory Model. And as you said there is no gurantee that getNum() ever sees something different from 0. In your case I would remove the synchronized and use a AtomicInteger instead. – eckes Jan 08 '17 at 14:43
  • internalNum is not static, there is no need to synchronize access to it since this itself is a Thread object and each will have their very own copy of internalNum. – AlexC Jan 08 '17 at 14:46
  • @AlexC you are almost correct. You do not know how I might have run the threads. I could have written: `Example myExample = new Example(); Thread t1 = new Thread(myExample); Thread t2 = new Thread(myExample); t1.start(); t2.start();` Of course, I wouldn't do that. – Marin Veršić Jan 08 '17 at 15:00
  • 1
    Yes based on the fact you made the base class a Thread instead of Runnable makes it unclear that you intend to invoke it as a Runnable while retaining all the extra weight that you add with inheriting from Thread. Now since the access to internalNum in your code is not synchronized, behavior is undefined. You need synchronized getter or use AtomicInteger. – AlexC Jan 08 '17 at 15:06
  • @eckes this is what interested me. So there is a chance that getNum() will never see anything else but 0, consequently producing a bug. I am aware of AtomicInteger, this was just an example. – Marin Veršić Jan 08 '17 at 15:07
  • in your scenario, modifyNum() is called once. So your limit condition `if (internalNum > 1)` should be rather `if (internalNum > 0)` since in any case, it is valued 0 or 1 but never more. – davidxxx Jan 08 '17 at 15:37
  • 1
    I think it would be spec compliant if you never see anything but 0, however I cannot think of any real world vm/architecture which behaves that way. There are some testvm flags which do behave that pathological. See for example here http://stackoverflow.com/a/17830049/13189 – eckes Jan 09 '17 at 10:26

1 Answers1

1

Let's say code execution is split in two threads. It doesn't exit. However a ressource (method, fields) may be accessed in concurrent way by two threads.

I think you mix things. Your class extends Thread but your question is about accessing to a resource of a same instance by concurrent threads.

Here is the code adapted to your question.

A shared resource between threads :

public class SharedResource{
    private int internalNum;

    public void getNum() {
        if (internalNum > 1)
            System.out.println(internalNum);
        else 
            System.out.println(1000);
    }

    public synchronized modifyNum() {
        internalNum += 1;
    }

    public void run() {
        // Some code
    }   
}

Threads and running code :

public class ThreadForExample extends Thread {

    private SharedResource resource;

    public ThreadForExample(SharedResource resource){
       this.resource=resource;
    }

    public static void main(String[] args){
       SharedResource resource = new SharedResource();
       ThreadForExample t1 = new ThreadForExample(resource);
       ThreadForExample t2 = new ThreadForExample(resource);
       t1.start();
       t2.start();
    }
}

Your question :

Hypothetically, following sequence of events occurs:

First thread accesses the getNum method and caches the internalNum which is 0 at the moment. At the very same time second thread accesses modifyNum method acquiring the lock, changes the internalNum to 1 and exits releasing the lock. Now, first thread continues it execution and prints the internalNum

In your scenario you give the impression that the modifyNum() method execution blocks the other threads to access to non synchronized methods but it is not the case.
getNum() is not synchronized. So, threads don't need to acquire the lock on the object to execute it. In this case, the output depends simply of which one thread has executed the instruction the first :

internalNum += 1;

or

 System.out.println(internalNum);
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • This is not what I was implying, I am full aware that modifyNum will not block other execution blocks and that getNum will not be blocked. This was just a hypothetical sequence of events that could have occurred. – Marin Veršić Jan 08 '17 at 15:20
  • Also, I am not quite certain you answer is correct. Refer to the comments on my post. What will be printed on the console is not determined solely on the order of execution. It is possible that `internalNum += 1;` executes first and 1000 gets printed nevertheless due to thread local caching of the first thread. – Marin Veršić Jan 08 '17 at 15:30
  • 1
    Ok I understand your interrogation. You mean getNum() is called by a thread and that the thread is stopped just after ? Yes in this case, I would say that the result is not really predictable as it depends on multiple things : CPU, hardware optimizations, OS, and the VM version. But anyway, without it you have already a not predictable behavior :) – davidxxx Jan 08 '17 at 15:55
  • Yes, something like that. First thread does not have to be necessarily stopped but stalled in some way while the second one executes uninterruptedly. – Marin Veršić Jan 08 '17 at 15:57