30

so i was testing with synchronized keyword. Here is an example that I tried:

public class MyTest {
    static int i = 0;
    public static void main(String[] args) {
        new Thread(t1).start();
        new Thread(t2).start();
    }

    private static void countMe(String name){
        i++;
        System.out.println("Current Counter is: " + i + ", updated by: " + name);
    }

    private static Runnable t1 = new Runnable() {
        public void run() {
            try{
                for(int i=0; i<5; i++){
                    countMe("t1");
                }
            } catch (Exception e){}

        }
    };

    private static Runnable t2 = new Runnable() {
        public void run() {
            try{
                for(int i=0; i<5; i++){
                    countMe("t2");
                }
            } catch (Exception e){}
       }
    };
}  

When I run it, the output of calling countMe() method from two threads generates this output:

Current Counter is: 1
Current Counter is: 2
Current Counter is: 4
Current Counter is: 5
Current Counter is: 6
Current Counter is: 7
Current Counter is: 3
Current Counter is: 8
Current Counter is: 9
Current Counter is: 10  

And when I change the method countMe() to:

private synchronized static void countMe(){
        i++;
        System.out.println("Current Counter is: " + i);
}  

I get this output:

Current Counter is: 1
Current Counter is: 2
Current Counter is: 3
Current Counter is: 4
Current Counter is: 5
Current Counter is: 6
Current Counter is: 7
Current Counter is: 8
Current Counter is: 9
Current Counter is: 10  

Although this gives me clear understanding the purpose of synchronized, I want to know is there any other reason as well, that we can use synchronized. Or what I have done here, is the only eason why we need the use of this synchronized keyword?

Thanks.

EDIT: Another thing that I am confused with is that in first output why the counter went to 3 after 7. It seems a bit impossible to me, but similar results do happen every time I try, is this normal?

911TurboS
  • 527
  • 2
  • 7
  • 14
  • There is no guarantee the threads will alternate. The thread which counted 3 may not print until after the other thread has counted to 7. – Peter Lawrey Feb 08 '12 at 16:00
  • thanks, but like after 7 how can the next call print 3. I mean it could print 6 or even5, but this seems like the new method call did not read i after it was 3, this sounds very strange. So you think it should be wise to always use synchronized whenever i have to use same block of code from 2 or more threads?? – 911TurboS Feb 08 '12 at 16:04
  • 5
    I suggest you print out the thread's name and it will become more obvious as to where the confusion is. Concurrency is a complex topic and there are no simple answers. My preference is to use actor patterns and share as little data between threads as possible. ideally, none. One thread performing code very efficiently is usually more than enough. – Peter Lawrey Feb 08 '12 at 16:09
  • You should not waste time trying to understand why you got 3 after 7. You are actually lucky that your code did not give you a lot more garbage; I would have expected to see some numbers twice. Badly synchronized code can do anything. Synchronization is a very intricate topic and it goes way beyond just using `synchronized` on methods that are called by two threads. I strongly recommend "Java Concurrency in Practice" by Lea et al. Or it's even better if you can avoid ever doing any multithreaded programming. – toto2 Feb 08 '12 at 16:20
  • toto2, i am trying to understand things, this is not production code of any sort. Thanks i will read that. @Peter Lawrey, intersting behavior, after printing out thread name, i see when using synzhronized, all the for loop is finished for first thread and only then the 2nd thread starts executing. Very strange, because i was thinking the access would be synchronized, not just the whole for loop, but here only after the first for loop finished, the 2nd thread starts execution. – 911TurboS Feb 08 '12 at 16:24
  • 2
    You might find this interesting http://java.sun.com/performance/reference/whitepapers/6_performance.html#2.1.1 Biases locking is one of the reasons you don't get "fair" access to shared resources. The common reason its not fair, is that completely fair access is very slow. Often so slow, you would be better off using one thread. For fair access you can use a `Lock lock = new ReentrantLock(true);` – Peter Lawrey Feb 08 '12 at 16:41
  • My guess is that CPU time (timeslice/priority) got reduced for one of the threads (maybe because CPU time was needed elsewhere by the OS). As such, it effectively got stalled. When the thread was allowed to run again, it had to play catch-up, and so printed a 3 behind the 7. Firing up 2 threads like this does not guarantee that they will alternate round-robin style perfectly. It's because there's always some jitter in a non-realtime environment. Try to print out `System.nanoTime()` in a loop and you'll see that the time difference can change between iterations. – Aquarelle Aug 11 '13 at 07:46

2 Answers2

26

Two things:

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

Synchronized methods enable a simple strategy for preventing thread interference and memory consistency errors: if an object is visible to more than one thread, all reads or writes to that object's variables are done through synchronized methods. (An important exception: final fields, which cannot be modified after the object is constructed, can be safely read through non-synchronized methods, once the object is constructed).

source: http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

vulkanino
  • 9,074
  • 7
  • 44
  • 71
  • vulkanino, then would it be wise idea to use synchronized every-time, if i use threads accessing same method. – 911TurboS Feb 08 '12 at 15:58
  • @911TurboS Just using `synchronized` on some methods is extremely insufficient for correctly synchronizing multithreaded code. You have to be aware of data consistency (and probably many other things). – toto2 Feb 08 '12 at 16:23
11

Vulkanino gave a good answer to your main question, so I'll only address your question about 3 printing after 7.

The 3 can print after the 7 because there is actually a lot more byte code in your statements than Java code.

I'll expand on that.

You call

System.out.println("Current Counter is: " + i);

and it occurs in one line of Java code, but really what happens is a string is created and then that string is passed to println. The println method itself has to do a bit of processing before it actually writes the line to the console.

Conceptually, something like the following is happening.

String printlnString = "Current Counter is: 3"
--> maybe the other thread executes here
System.out.println(printlnString);
--> or maybe the other thread executes here
i is now equal to 7 and the console has "Current Counter is: 7"
println writes "Current Counter is: 3" to console
David V
  • 11,531
  • 5
  • 42
  • 66
  • Instead of writing the counter to out, write it into a simple array, then when the threads are finished, print out the array. – cognacc Apr 03 '13 at 14:45