简体   繁体   中英

Is there any functional difference between a 'synchronized function' and a 'synchronized block inside the function'?

Is there any difference between the two :

public void synchronized func() {
}

and

public void func() {
   synchronized(this) {
   }
}

I understand in the first case the whole function func is synchronized and in the second case only a part of the function's code is synchronized. But does it make a difference ? I mean instructions are always executed one after the another. It won't be that if a thread is unable to acquire the lock as it reaches the synchronized block, it will start the operations after the synchronized block !

Is there any functional difference at all or it is just a good practice ?

in the second case only a part of the function's code is synchronized. But does it make a difference ?

Yes, it does make a difference if there is a lot of code outside of the synchronized part.

public void func() {
   someHeavyOperations();
   synchronized(this) {
      criticalSectionOperations();
   }
   someMoreHeavyOperations();
}

You want to keep your critical section as small as possible.

It won't be that if a thread is unable to acquire the lock as it reaches the synchronized block, it will start the operations after the synchronized block !

No, but it will have been able to finish the operation before the synchronized block, and it also does not keep anyone waiting while it is doing the operations after the synchronized block.

Even if there is no more code outside the synchronized block, the construct is useful, as you can synchronize on other things than this , for example to keep the lock private or more granular .

I don't think there's any practical difference in the above.

However , I would favour the latter since it's more flexible. Instead of locking on the containing object ( this ) you could lock on a particular lock object, and different methods could specify different lock objects (depending on the synchronisation requirements). This means you can tune the synchronisation to be more fine-grained as/when you need it.

eg

public synchronized void doSomething() {
   ...
}

(locks on this )

vs.

public void doSomething() {
   synchronized(someLockObject) {
      ...
   }
}

From a locking point of view there is no difference.

From a byte code point of view, you can tell the first method is synchronized using reflection or most class viewers. The second case is harder to determine it is the case.

The block synchronization is preferred if you want to hide the lock used, ie you are not using this so a caller cannot confuse things.

You need to keep Critical Section as small as possible. so synchronized(this) is more useful Critical Section as small as possible. so synchronized(this) is more useful

But if your critical section is your method then there you can go ahead and declare method as synchronized

From 1.5 onwards you can always use ReentrantLock

A Reentrant mutual exclusion Lock with the same basic behaviour and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.

class X {
private final ReentrantLock lock = new ReentrantLock();
// ...

public void m() { 
  lock.lock();  // block until condition holds
  try {
    // ... method body
  } finally {
    lock.unlock()
  }
}
}

If you're synchronizing the entire contents of the method then there is no functional difference. If you're only synchronizing a part of the method then the scope of the sycnrhonization is different.

You're correct that the code after the synchronized block will not be executed before the synchronized block. However threads can be swapped out at any point that's not synchronized. Consider the following:

synchronized (this) {
    //sycnronized bit
}
// some other code

If there are two threads, A & B and A gets the lock B will block until A exits the synchronized block. However A could be swapped out as soon as it exits this block. B could then enter the synchronized block and complete the function before A gets swapped back in.

This can be used quite commonly, eg to ensure a mutable instance variable is consistent for the scope of a calculation while allowing multiple threads to perform the expensive calculation. Eg

Object localCopy;
synchronized (this) {
    localCopy = this.instanceVar;
}
// expensive calculation using localCopy which won't change even if instanceVar is changed.

to quote JLS http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.3.6

Thus, the code:

synchronized void bump() {
    count++;
}

has exactly the same effect as:

void bump() {
    synchronized (this) { count++; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM