user697911
user697911

Reputation: 10551

Synchronization of counter size()

The printed result is -20000000, which proves that I don't need to synchronize the size() method. But my understanding is that I should synchronize size() too. What's the real situation here?

public class Test {
    public static void main(String[] args){
        Counter c = new Counter();
        T1 t1 = new T1(c);
        T1 t2 = new T1(c);
        t1.start();
        t2.start();

        try{
            t1.join();
            t2.join();
        } catch (Throwable t) {

        }

        System.out.println("c=" + c.size());
    }   
}

class T1 extends Thread{
    private Counter c;

    public T1(Counter c){
        this.c = c;
    }
    public void run(){
        for(int i=0; i<10000000; i++){
            c.decrement();
        }
    }
}

class Counter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }
    public synchronized void decrement() {
        c--;
    }
    public int size() {
        return c;
    }
}

Upvotes: 0

Views: 97

Answers (3)

Ryan Stewart
Ryan Stewart

Reputation: 128879

proves that I don't need to synchronize the size() method

Actually, it only proves that your counter can work sometimes. size() should be synchronized because, basically, the field it accesses could be cached in multiple registers simultaneously, and without any kind of indication that the field is shared among threads, several threads could be working on purely local copies of the same field without ever syncing back to main memory.

The fact that you don't see any problems when you run it is only proof that writing concurrent code in this style is a bad idea.

Upvotes: 0

ZhongYu
ZhongYu

Reputation: 19682

It does work in your example, because after t1.join() and t2.join(), any changes made in t1 and t2 are visible to the main thread.

quoting http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.4

The final action in a thread T1 synchronizes-with any action in another thread T2 that detects that T1 has terminated.

T2 may accomplish this by calling T1.isAlive() or T1.join().

Basically, when a thread starts, it should see all prior changes; after a thread finishes, what it has changed should be visible to others. If thread A starts B, and thread C joins thread B

    A         B        C
    |
    w1
    |
 start B --> o
    .        |
    .        |
    .        r1       .
             w2       .
             |        .       
             |        |
             o -->  join B
                      |
                      r2
                     

It's guaranteed that r1 sees w1, r2 sees w2.

Upvotes: 1

Gee_Djo
Gee_Djo

Reputation: 243

it will be good if you write

public int  synchronized size()

Upvotes: 0

Related Questions