views:

183

answers:

7

Hi,

Assume the following class

public class TestObject{
    public void synchronized method1(){
        //some 1000 lines of code
    }

    public void method2(){
        //some 1000 lines of code
    }
}

Let's assume there are two threads accessing the same instance of TestObject class, let's call them t1 and t2. I want to know what will happen in the following scenario.

  1. When t1 is in midway of accessing method1(). Now t2 is trying to access method2().
  2. When t1 is in midway of accessing method2(). Now t2 is trying to access method1().

My understanding is that for the first question, the thread t2 will not be given permission as the object will be locked by t1. For the second question, the thread t2 will be granted access and takes lock on the object and will stall t1 from execution. But my assumption was wrong. Can anyone explain this?

Thanks

A: 

method1() is synchronized and hence called as thread safe method. When multiple threads try to access this method simultaneously then only the lock on instance object will work.

method2() is not synchronized and hence is a thread unsafe method, other threads can call this method even if the some other thread has lock on the instance, that is why this method is called as thread unsafe method.

In both cases you mentioned one thread will get lock on the instance by calling method1() and other thread will try to access method2() which is unsafe and hence both thread will execute.

with regards
Tushar Joshi, Nagpur

Tushar Joshi
Whether a method is thread-safe or not does not depend on the `synchronized` keyword. It depends on the way the method is written. A method without the keyword can absolutely be thread-safe if it doesn't access any properties in a thread-unsafe manner.
Erick Robertson
I agree with @Erick that a method can be still thread safe if it is written carefully that is not using any properties. But when a method is defined as synchronized it always becomes thread safe. In my experience usually developer writes a method without synchronized keyword and also carefully to keep it safe, but later on due to some bug fix or new feature that method gets modified and rendered unsafe by new developers working on that project. Using synchronized block is a way to keep access to properties safely in such cases.
Tushar Joshi
I would agree that this is true so long as none of the `synchronized` methods contain any blocking calls like `Thread.sleep()` or have `synchronized` code blocks within them which require locks on any other object.
Erick Robertson
+2  A: 

One method is synchronized while the other is not. So no matter whether the lock on an Object (in this is case the instance the method belongs to) has been acquired or not, the non-synchronized method will execute unimpeded (since it foes not try to acquire or wait for a lock). Which means in both cases both threads will run without waiting for each other - resulting in a possibly inconsistent state of the object.

MAK
You should be sure, because you are right - except that there's no reason to assume a "possibly inconsistent state" in the object, because you don't know what the methods do.
Erick Robertson
@Erick Robertson: Hence the word "possibly" :). Thanks, I am removing the "not sure" part now.
MAK
+6  A: 

Only the method with the keyword synchronized holds a lock for this object when a thread is running in that method.
Had both method 1 and method 2 been declared as synchronized, one thread would block the other even though they are trying to run different methods.
In your example only 1 method is blocking by an implicit lock.
As a result t1 and t2 can be running concurrently in method 1 and method 2 (or vice versa).
Only when trying to access method 1, a t1 or t2 would block if the lock has already been acquired

A: 

Removed because goofballs can't read

dwb
-1 This is incorrect.
Erick Robertson
@Erick Robertson: give details. which part is incorrect?
dwb
t2 will not wait until t1 has exited method1() because method2() is not synchronized.
Erick Robertson
You said that the result of #1 is that t2 will wait until t1 has exited method1 before gaining access to method1. But t2 is not trying to access method1, so t2 will not wait.
Erick Robertson
A: 

Both threads will execute as if the lock does not exist.

The thread accessing method2 will never know that it should get a lock so it will execute even if an other thread holds the lock. Only synchronized methods will reserve the lock since synchronization is optional and not always wanted or even necessary.

If both threads execute method1 one of them will block until the other thread exits the method and releases the lock.

To make sure only one thread executes and all others wait you have to make both methods synchronized.

josefx
I just have an objection to "as if the lock does not exist". No thread will ever execute as if locks don't exist.
Erick Robertson
@Erick Robertson, if the lock is not checked it may as well not exist, in both examples only one thread checks the lock so it will always get it and the other thread ignores it. None of the threads will block, no state is protected.
josefx
+3  A: 

In both cases, the second thread will be given permission to execute its method.

Since only one of these two methods contains the synchronized keyword, both of these methods can be executed simultaneously. The restriction is that only one method with that keyword can be executed at any given time, because executing that method requires a lock on the object. A thread without the keyword requires no lock and will always be allowed to execute regardless of the object being locked.

I also assume here that the 1000 lines of code in method2 does not contain a block like this:

synchronized (this) {
}

If it does, then the results will be different.

  1. t2 will be allowed to start executing the method. When it hits the synchronized line, it will wait for t1 to finish method1 and release its lock before continuing.
  2. t2 will be allowed to start executing the method so long as t1 is not within the synchronized code block. If it is, t2 will wait until t1 exits the block and releases the lock before beginning. If t1 has not yet entered the synchronized block, then t2 will acquire the lock, and t1 will wait as soon as it gets to the synchronized code block until t2 completes the method and releases the lock.
Erick Robertson
+2  A: 

When you declare a method to be synchronized, e.g.:

public synchronized void foo() {
    // Do something
}

the compiler treats it as though you had written this:

public void foo() {
    synchronized (this) {
        // Do something
    }
}

In your example you have one synchronized method and one non-synchronized. This means that only access to method1 will be locked. Locking checks are only done on entry to a synchronized block, so calling method2 will not trigger any locking.

To answer your two questions, then, in both cases the two threads will be allowed to proceed because they are not trying to obtain a lock on the same object. If you declare method2 to be synchronized (or manually add a synchronized (this) block) then one thread will be forced to wait for the other.

Remember: synchronizing on an object does not prevent other threads calling methods on that object. It only prevents another thread entering a synchronized block with the same lock object.

Incidentally, it's often better to have an internal lock object rather than declaring methods to be synchronized, e.g.

class Foo {
    private final Object LOCK = new Object();
    public void doSomething() {
        synchronized (LOCK) {
            // Whatever
        }
    }
}

Otherwise I can break your thread-safety by doing this:

class MessEverythingUp {
    public MessEverythingUp(Foo foo) {
        synchronized (foo) {
            while (true) {
                System.out.println("Pwnd ur thread safety");
            }
        }
    }
}

Since I'm locking the instance of foo, your synchronized methods (with their implicit "synchronized (this)") will not be able to obtain a lock and will block forever. Most importantly, you cannot prevent this because I can synchronize on whatever object I like. Obviously the example is extreme but you can get nasty, subtle deadlock bugs if you're not careful about this sort of thing.

Cameron Skinner