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();
}
}
Synchronized on the method declaration is syntactical sugar for this:
public void addA() {
synchronized (this) {
a++;
}
}
On a static method it is syntactical sugar for this:
ClassA {
public static void addA() {
synchronized(ClassA.class) {
a++;
}
}
I think if the Java designers knew then what is understood now about synchronization, they would not have added the syntactical sugar, as it more often than not leads to bad implementations of concurrency.
From "The Java™ Tutorials" on synchronized methods:
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.
From "The Java™ Tutorials" on synchronized blocks:
Synchronized statements are also useful for improving concurrency with fine-grained synchronization. Suppose, for example, class MsLunch has two instance fields, c1 and c2, that are never used together. All updates of these fields must be synchronized, but there's no reason to prevent an update of c1 from being interleaved with an update of c2 — and doing so reduces concurrency by creating unnecessary blocking. Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks.
(Emphasis mine)
Suppose you have 2 non-interleaving variables. So you want to access to each one from a different threads at the same time. You need to define the lock not on the object class itself, but on the class Object like below (example from the second Oracle link):
public class MsLunch {
private long c1 = 0;
private long c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void inc1() {
synchronized(lock1) {
c1++;
}
}
public void inc2() {
synchronized(lock2) {
c2++;
}
}
}
The lock accessed is on the object, not on the method. Which variables are accessed within the method is irrelevant.
Adding "synchronized" to the method means the thread running the code must acquire the lock on the object before proceeding. Adding "static synchronized" means the thread running the code must acquire the lock on the class object before proceeding. Alternatively you can wrap code in a block like this:
public void addA() {
synchronized(this) {
a++;
}
}
so that you can specify the object whose lock must be acquired.
If you want to avoid locking on the containing object you can choose between:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With