3

I saw this on Java's synchronization tutorial:

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

The tutorial says that subsequent calls by different threads block, like so:
A: increment();
B: increment(); <-- has to wait for A to finish

...but say two threads interleave like so:
A: increment(): load c -> 0;
B: decrement(): load c -> 0;
A: increment(): increment c -> 1;
B: decrement(): decrement c -> -1;
A: increment(): save c -> 1;
B: decrement(): save c -> -1;
Finally: c == -1;

Neither has to wait, in my understanding of the tutorial; so is c protected from memory inconsistency?

Due to popular demand: the tutorial

7
  • 2
    Please provide a link to the tutorial. Are there other methods, load and save, which are part of the class but which are not synchronized? Commented Jun 30, 2014 at 20:13
  • comparing the tutorial to your question, the material about threads calling the various methods in your question is not in the tutorial so where did that come from? Did you write an actual Java program to try it out and those are the results or what? Commented Jul 1, 2014 at 12:54
  • I just made up two threads calling increment() and decrement() specifically because it wasn't in the tutorial; I wanted insight on what goes on because it wasn't in the tutorial. Commented Jul 1, 2014 at 21:41
  • could you post the source for that? How many lines of code is your example? Commented Jul 1, 2014 at 22:03
  • There's no source, completely pulled out those exact two lines from thin air to understand what's happening. Commented Jul 2, 2014 at 18:31

3 Answers 3

4

The synchronized keyword on a (non-static) method causes the method to lock on the enclosed object - in this case the instance of SynchronizedCounter. Thus each synchronized method prevents each other synchronized method from running in another thread. So in your example, thread A's call to increment() would complete before thread B's call to decrement() would start. Therefore your class is thread safe.

1
  • Did you mean "enclosing" object? As in, the object that the method belongs to? Because the only thing enclosed in the synchronized method is the int c... or is c wrapped in an object and then locked? Commented Jul 1, 2014 at 21:44
0

Your current understanding of Java threading is not complete. In Java, every object has an "implicit lock", which is the object itself. And the "synchronized" keywords uses this implicit lock to make your code thread safe. The following code has the same effect:

public class SynchronizedCounter {
    private int c = 0;

    public  void increment() {
        synchronized(this){
            c++;
        }
    }

    public  void decrement() {
        synchronized(this){
            c--;
        }
    }

    public  int value() {
        synchronized(this){
           return c;
        }
    }
}

Also, if you want to, you can declare multiple objects to server as simple lock objects. This, again, has the same effect:

public class SynchronizedCounter {
    private int c = 0;
    private Object lockObject;
    public  void increment() {
        synchronized(lockObject){
            c++;
        }
    }

    public  void decrement() {
        synchronized(lockObject){
            c--;
        }
    }

    public  int value() {
        synchronized(lockObject){
           return c;
        }
    }
}

Before synchronized method or synchronized block (also sometimes called guarded block) is executed, the executing thread must first obtain the lock. When it doesn't, the code in synchronized methods/block won't be executed. That's why your example will always work, because all the methods share one lock (the implicit one), none of the methods can be executed simultaneously with other methods.

For advanced locking mechanisms, please see the Lock interface.

0

Yes, c is protected from memory inconsistency.

However if the value of c is copied out of the object via the getter method, then that copy will live separately and become out of date.

None of the methods on SynchronizedCounter can be invoked simultaneously because they are all declared synchronised.

This does two things, firstly it creates a mutually exclusive region by requiring any thread to enter a monitor (http://en.wikipedia.org/wiki/Monitor_%28synchronization%29) and secondly it places memory barriers (http://en.wikipedia.org/wiki/Barrier_%28computer_science%29) at the entry to the method and the exit.

Thus only one thread can modify c at a time, and no matter which thread on which CPU core is executing that method then it will read the latest value of c and will make its change to c visible to the next thread that enters the monitor. For more on the memory barrier side, Doug Lea wrote a very useful guide at http://gee.cs.oswego.edu/dl/jmm/cookbook.html.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.