Java Synchronized Method Lock on Object, or Method

Java synchronized method lock on object, or method?

If you declare the method as synchronized (as you're doing by typing public synchronized void addA()) you synchronize on the whole object, so two thread accessing a different variable from this same object would block each other anyway.

If you want to synchronize only on one variable at a time, so two threads won't block each other while accessing different variables, you have synchronize on them separately in synchronized () blocks. If a and b were object references you would use:

public void addA() {
synchronized( a ) {
a++;
}
}

public void addB() {
synchronized( b ) {
b++;
}
}

But since they're primitives you can't do this.

I would suggest you to use AtomicInteger instead:

import java.util.concurrent.atomic.AtomicInteger;

class X {

AtomicInteger a;
AtomicInteger b;

public void addA(){
a.incrementAndGet();
}

public void addB(){
b.incrementAndGet();
}
}

Is Java synchronized lock key the same for all synchronized methods of the same object?

Yes, you are correct.
For a more elaborate answer, read on.

From my favourite book on Java, Head First Java

Every object has a lock. Most of the time, the lock is unlocked, and
you can imagine a virtual key sitting with it. Object locks come into
play only when there are synchronized methods.

When an
object has one or more synchronized methods, a thread can enter a
synchronized method only if the thread can get the key to the object’s
lock!

The locks are not per method, they are per object.
If an object has two synchronized methods, it does not simply mean that you
can’t have two threads entering the same method. It means you can’t
have two threads entering any of the synchronized methods.


Think about it. If you have multiple methods that can potentially act
on an object’s instance variables, all those methods need to be
protected with synchronized.
The goal of synchronization is to
protect critical data. But remember, you don’t lock the data itself,
you synchronize the methods that access that data.

So what
happens when a thread is cranking through its call stack (starting
with the run() method) and it suddenly hits a synchronized method?

The thread recognizes that it needs a key for that object before
it can enter the method. It looks for the key (this is all handled by
the JVM; there’s no API in Java for accessing object locks), and if
the key is available, the thread grabs the key and enters the method.

From that point forward, the thread hangs on to that key like the
thread’s life depends on it. The thread won’t give up the key until it
completes the synchronized method.
So while that thread is holding
the key, no other threads can enter any of that object’s synchronized
methods, because the one key for that object won’t be available.


TL;DR:

Every Java object has a lock.

That is, the locks are not per method, they are per object.
Now, a lock has only one key.

Most of the time, the lock is
unlocked and nobody cares.
But if an object has
synchronized methods, a
thread can enter one of the
synchronized methods ONLY
if the key for the object’s lock
is available. In other words,
only if another thread hasn’t
already grabbed the one key(by entering a synchronized function).

Synchronized block with private immutable object and synchronize method difference

With

public class Example {
public void synchronized getCount() {
...
}
}

it's synchronizing on current object this. Other class is able to get the reference of current object and use it as monitor lock:

public class OtherClass {

public void otherMethod() {
Example example = new Example();
synchronized (example) {
...
}
}
}

This might get unexpected results, for example, causing getCount blocked when otherMethod being executed.

With the first approach, since the monitor lock lockObject is private, other class is not able to access it directly, so it's preferred over the second approach.

Synchronized lock by particular ID

It seems like a bad idea because:

  1. String#intern() is a native method and it uses native Hash Table which apparently is much slower than a typical ConcurrentHashMap.
  2. You probably don't want the pool of strings to grow indefinitely. How would you invalidate entries from there?

I think using your own map of strings will be the preferred way. Maybe Guava's cache could be leveraged because you need to evict items from that map eventually. But this needs further research.

Leasing a lock

Another option is to have a set of predefined lock objects. E.g. a HashMap of size 513. Then to acquire a lock use bid.hashCode() mod 513:

int hash = Math.abs(bid.hashCode() % 513);
Object lock = locks.get(hash);
synchronized(lock) {...}

This will occasionally lock unrelated transactions, but at least you don't have to bother with eviction.

PS: there was some method to calculate a true mod in Math class.

What happens if a thread executing a synchronised method suspends?

No, it won't force the thread to relinquish the lock since it would break the whole idea of synchronization. It is possible to exit the monitor via wait method call on the object (declared in the Object class) to give other threads a chance to enter it .

Why do we need to specify the lock for synchronized statements?

Given that there's only one lock for each instance of a class, then why doesn't Java just allow us to do this:

void method() {
synchronized {
// do something
}

// do other things
}

Although an intrinsic lock is provided with each instance,
that's not necessarily the "obvious" lock to use.

You're perhaps right that they could have provided synchronized { ... } as a shorthand for synchronized (this) { ... }.
I don't know why they didn't, but I never missed it.
But concurrent programming is tricky,
so making the lock object an explicit required parameter may make things clearer to readers, which is a good thing, as @ajb pointed out in a comment.
In any case, I don't think syntax is your main question, so let's move on.

What's the purpose of specifying a lock?

Uhm, the lock is perhaps the single most important thing in the synchronization mechanism. The key point in synchronization is that only one thread can hold the same lock. Two threads holding different locks are not synchronized. So knowing what is the lock guarding the synchronization is crucial.

Does it make a difference if I choose one object as a lock over the other?

I hope the previous section makes it clear that yes, you have to choose the object carefully. It has to be an object visible by all threads involved,
it has to be not null, and it has to be something that won't get reassigned during the period of synchronization.

Or could I just choose any random object?

Certainly not. See the previous section.

To understand concurrency in Java, I recommend the book Java Concurrency in Practice by one of the authors of the API, or Oracle's tutorials on the subject.

Why is synchronized block better than synchronized method?

It's not a matter of better, just different.

When you synchronize a method, you are effectively synchronizing to the object itself. In the case of a static method, you're synchronizing to the class of the object. So the following two pieces of code execute the same way:

public synchronized int getCount() {
// ...
}

This is just like you wrote this.

public int getCount() {
synchronized (this) {
// ...
}
}

If you want to control synchronization to a specific object, or you only want part of a method to be synchronized to the object, then specify a synchronized block. If you use the synchronized keyword on the method declaration, it will synchronize the whole method to the object or class.



Related Topics



Leave a reply



Submit