4

I am preparing for the OCP exam and I found this question in a mock exam:

Given:

class Calculator {
    private AtomicInteger i = new AtomicInteger();
    public void add(int value) {
        int oldValue = i.get();
        int newValue = oldValue + value;
        System.out.print(i.compareAndSet(oldValue,newValue));
    }
    public int getValue() {
        return i.get();
    }
}

What could you do to make this class thread safe?

And surprisingly, to me, the answer is: "Class Calculator is thread-safe"

It must be that I have not understood correctly the concept. To my understanding, a class is thread safe when all the methods work as expected under thread concurrency. Now, if two thread call at the same time getValue(), then call add() passing a different value, and then they call getValue() again, the 'second' thread won't see its value passed increased.

I understand that the oldValue and the newValue as local variables are stored in the method stack, but that doesn't prevent that the second call to compareAndSet will find that the oldValue is not the current value and so won't add the newValue.

What am I missing here?

daniel sp
  • 937
  • 1
  • 11
  • 29
  • 4
    This is hardly a dupe since the fundamental question is about the threadsafety of `AtomicInteger`. It's a trivial but different question. – pvg Jan 16 '16 at 21:48
  • 1
    Of course I have looked. It adds the second parameter only if the internal value in the AtomicInteger is equals to the first parameter. This has nothing to do with the question here. The second call to the add method will fail. To me it means that this class is not thread safe. – daniel sp Jan 16 '16 at 21:49
  • I changed the tittle. I had seen also that thread and it only reaffirmed me that this code is not thread safe. It is not predictable (the add, not the compareAndSet which is out of any doubt here) when two threads call at same time. – daniel sp Jan 16 '16 at 21:54
  • It is thread safe since we need to worry about the state. The fact that `add` method "fails" the second time if called twice simultaneously is just part of the mechanism of using AtomicInteger. Of course you could do it otherwise with synchronised, but this way of doing it is perfectly valid. – vikingsteve Jan 16 '16 at 22:26
  • 6
    Well from a software engineering perspective, I have to agree with OP. Although the code seems to be "thread-safe" formally, when I have a class `Calculator` with a method `add`, should the value not be added in every possible case? otherwise I would call it `tryAdd` or something like that. – user140547 Jan 16 '16 at 22:36
  • 3
    And another one of those ridiculous OCP questions. If you don't define what you mean by thread safe the whole question is pointless. [The great Eric Lippert said it much better than I ever could](https://blogs.msdn.microsoft.com/ericlippert/2009/10/19/what-is-this-thing-you-call-thread-safe/) – Voo Jan 16 '16 at 22:52
  • @Voo Indeed. I am going to say that the OCP is wrong here. The *class* is thread-safe, but the *method* is not, by any rational definition, as its output varies indeterminately with concurrency, and it is also very poorly designed. The `add()` method shouldn't print its own result, and it should return the result of `compareAndSet()` so the caller knows what has happened. – user207421 Jan 16 '16 at 23:14
  • @EJP Well it's not really "wrong". Thread-safety is part of the API contract and can be defined how the designer thinks is best for the targeted use cases. There are scenarios where swallowing some action and just not throwing any exceptions or becoming internally inconsistent is perfectly acceptable and might be considered "thread-safe". In this case though? I'd say either rename it to `tryAdd` and return the bool, or - much better really - use the AtomicInteger's `add` method (or if you need more code, do the standard loop around the CAS). – Voo Jan 16 '16 at 23:21
  • 1
    @Voo Indeed, again, but in that case the question itself is pointless, which I guess we agree on. Absent a specification of what it means, any code or class can be thread-safe, and there is therefore no particular reason for the OCP to assert that the class itself is already thread-safe: which already makes it wrong. But I'm talking about thread-safety under 'any rational definition', and this code fails that test. – user207421 Jan 16 '16 at 23:50
  • It is a pretty bad question. Yes, on a very basic level you may call the code thread-safe, but that level is almost completely meaningless in practice. It's got a quite blatant race condition in there, which affects the output state of the object (because `add()` isn't idempotent), and very few applications would genuinely need that kind of behaviour. – biziclop Jan 17 '16 at 00:15
  • @EJP I do think we are in violent agreement on all points. I'm reasonably sure that you also agree that a question that relies on subjective definitions should not be part of an exam (at least not if you could thoroughly define the issue). I'm always wondering is the quality of the OCP exam really this bad or are the questions you see once in a while here just questions made up by people as preparation for the real deal? – Voo Jan 17 '16 at 14:46
  • I'd expect OCP exam to provide at least a one-line Javadoc comment about what the `add` method is supposed to do. For instance, it is not clear if the method is supposed to notice an intermediate modification that result in keeping the observed `oldValue` the same ( cf. [ABA problem](https://en.wikipedia.org/wiki/ABA_problem) ). It appears that we are not concerned with the ABA problem here, but to make that assumption concrete, Javadoc is necessary. – Kedar Mhaswade Jan 17 '16 at 15:32

4 Answers4

3

According to JCIP

A class is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code.

Although there is no definition of thread-safety and no specification of the class, in my opinion, by any sane definition of an add method in a Calculator class it is "correct" if it the value of the AtomicInteger i is increased in any case, "regardless of the scheduling or interleaving of the execution".

Therefore, in my opinion, the class is not thread-safe by this definition.

user140547
  • 7,750
  • 3
  • 28
  • 80
  • The OP's question is "Why is this code thread safe?" and "What am I missing here?". Your answer doesn't really answer those questions. (You seem to be trying to imply that the code is *not* thread-safe, and that the OP *isn't* missing anything. That would be a perfectly fine answer; but it's not enough to imply it, you need to actually say it.) – ruakh Jan 16 '16 at 23:33
  • I think a better way of phrasing that would be "behaves predictably" as "correctly" begs for all sorts of subjective evaluations. The example class is thread-safe because the method either performs addition or it cleanly fails. I agree that this really isn't "proper" behavior from a design standpoint, but it is sufficiently predictable to qualify as thread-safe. – Jim W Jan 17 '16 at 05:04
3

There is clearly a problem with the term "thread-safe" here, in that it isn't absolute. What is considered thread-safe depends on what you expect the program to do. And in most real-world applications you wouldn't consider this code thread-safe.

However the JLS formally specifies a different concept:

A program is correctly synchronized if and only if all sequentially consistent executions are free of data races.

If a program is correctly synchronized, then all executions of the program will appear to be sequentially consistent

Correctly synchronized is a precisely defined, objective condition and according to that definition the code is correctly synchronized because every access to i is in a happens-before relationship with every other access, and that's enough to satisfy the criteria.

The fact that the exact order of reads/writes depends on unpredictable timing is a different problem, outside the realms of correct synchronization (but well within what most people would call thread safety).

biziclop
  • 48,926
  • 12
  • 77
  • 104
2

The add method is going to do one of two things:

  • Add value to the value of i, and print true.
  • Do nothing and print false.

The theoretically sound1 definitions of thread-safe that I have seen say something like this:

Given a set of requirements, a program is thread-safe with respect to those requirements if it correct with respect to those requirements for all possible executions in a multi-threaded environment.

In this case, we don't have a clear statement of requirements, but if we infer that the intended behavior is as above, then that class is thread-safe with respect to the inferred requirements.

Now if the requirements were that add always added value to i, then that implementation does not meet the requirement. In that case, you could argue that the method is not thread-safe. (In a single-threaded use-case add would always work, but in a multi-threaded use-case the add method could occasionally fail to meet the requirement ... and hence would be non-thread-safe.)


1 - By contrast, the Wikipedia description (seen on 2016-01-17) is this: "A piece of code is thread-safe if it only manipulates shared data structures in a manner that guarantees safe execution by multiple threads at the same time." The problem is that it doesn't say what "safe execution" means. Eric Lippert's 2009 blog post "What is this thing you call thread-safe" is really pertinent.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
0

It's threadsafe because compareAndSet is threadsafe and that's the only part that's modifying shared resources in the object.

It doesn't make a difference how many threads enter that method body at the same time. The first one to reach the end and call compareAndSet "wins" and gets to change the value while the others find the value has changed on them and compareAndSet returns false. It never results in the system being in an indeterminate state, though callers would probably have to handle the false outcome in any realistic scenario.

Jim W
  • 492
  • 2
  • 11
  • 1
    If the 'add' method would return a boolean with the result of the compareAndSet I would agree with you. – daniel sp Jan 16 '16 at 22:56
  • 1
    Oh nevermind, I see what you meant. Yeah, it's impossible to handle the false outcome if it's never returned. So yeah, this code, if part of a real application and not a contrived exam scenario, would definitely raise eyebrows. – Jim W Jan 16 '16 at 22:59