synchronized block for an Integer object

2019-01-09 17:12发布

问题:

I just came across the synchronized block in Java and wrote a small programm to test how it works.

I create 10 threads and let each thread increment an Integer object 1000 times.

So with synchronization I would assume a result of 10000 after all threads have finished their work and a result of less than 10000 without synchronization .

However the synchronization is not wokring as I expected.

I guess it has something to do with immutability of the object or so.

My program:

public class SyncTest extends Thread{

    private static Integer syncObj = new Integer(0);
    private static SyncTest[] threads = new SyncTest[10];

    private boolean done = false;

    public void run(){
        for(int i = 0; i < 1000; i++){
            synchronized(syncObj){
                syncObj ++;
            }
        }
        done = true;
    }

    public static void main(String[] args) {

        for(int i=0; i < threads.length; i++){
            threads[i] = new SyncTest();
            threads[i].start();
        }

        while(!allDone()); //wait until all threads finished

        System.out.println(syncObj);
    }

    private static boolean allDone(){
        boolean done = true;
        for(int i = 0; i < threads.length; i++){
            done &= threads[i].done; 
        }

        return done;
    }
}

Can someone clarify this?

回答1:

syncObject is changing each time you ++ it (the ++ is converting it to a primitive int, incrementing it, and then autoboxing it back to the Integer object. Integer objects are immutable ... once they are created, they cannot change.

Bottom ine is that you are not using the same syncPObj in all the threads, different threads use different syncObjects at different times to sync on.

use one object as the synchronization (call it syncObj), and declare it as a final Object:

private static final Object syncObject = new Object(); 

Then your counter should be a primitive (int) for perofrmance, call it 'counter' or something.

Synchronize on syncObject, and increment counter.

Edit: as per @jsn, the done flag is also broken in that your code has a 'tight loop' on the isAllDone() method, and that is bad practice. You should use thread[i].join() to wait (blocking) on each thread's completion, and then check the status from that. Using an ExecutorService is the 'right way'.



回答2:

As assumed it is because of the immutability of the Integer object.

I've changed the synchonized block to

Integer old = syncObj;
syncObj ++;
System.out.println(syncObj == old);

and my console gets filled with falses

So each time I increment the Integer a new object is createt.

Therefore I only read from the old Object and it will not be locked.



回答3:

These operations are usually done with Atomic. Have a look here. These structures are specifically designed for multi-threaded computation. Normal implementations are not thread safe.