Если у меня есть 2 синхронизированных метода в том же классе, но каждый получающие доступ различные переменные, действительно ли 2 потока могут получить доступ к тем 2 методам одновременно? Блокировка происходит на объекте, или это становится столь же конкретным как переменные в синхронизированном методе?
Пример:
class X {
private int a;
private int b;
public synchronized void addA(){
a++;
}
public synchronized void addB(){
b++;
}
}
Могут 2 потока получать доступ к тому же экземпляру выполнения класса X x.addA(
) и x.addB()
одновременно?
Если вы объявляете метод как синхронизированный (как вы это делаете, набирая public synchronized void addA ()
), вы синхронизируете весь объект , поэтому два потока, обращающиеся к другой переменной из этого же объекта, в любом случае будут блокировать друг друга.
Если вы хотите синхронизировать только одну переменную за раз, чтобы два потока не блокировали друг друга при доступе к разным переменным, вы должны синхронизировать их отдельно в блоках synchronized ()
. Если бы a
и b
были ссылками на объекты, вы бы использовали:
public void addA() {
synchronized( a ) {
a++;
}
}
public void addB() {
synchronized( b ) {
b++;
}
}
Но поскольку они примитивны, вы не можете этого сделать.
Я бы посоветовал вам вместо этого использовать AtomicInteger :
import java.util.concurrent.atomic.AtomicInteger;
class X {
AtomicInteger a;
AtomicInteger b;
public void addA(){
a.incrementAndGet();
}
public void addB(){
b.incrementAndGet();
}
}
Синхронизация в объявлении метода является синтаксическим сахаром для этого:
public void addA() {
synchronized (this) {
a++;
}
}
Для статического метода это синтаксический сахар для этого:
ClassA {
public static void addA() {
synchronized(ClassA.class) {
a++;
}
}
Я думаю, если бы разработчики Java знали тогда, что понимается сейчас о синхронизации, они не добавили бы синтаксический сахар, так как это чаще всего приводит к плохой реализации параллелизма.
Доступ к блокировке осуществляется на объекте, а не на методе. Какие переменные доступны в методе, не имеет значения.
Добавление «синхронизировано» к методу означает, что поток, выполняющий код, должен получить блокировку объекта перед продолжением. Добавление «статической синхронизации» означает, что поток, выполняющий код, должен получить блокировку объекта класса перед продолжением. В качестве альтернативы вы можете заключить код в такой блок:
public void addA() {
synchronized(this) {
a++;
}
}
, чтобы вы могли указать объект, блокировка которого должна быть получена.
Если вы хотите избежать блокировки содержащего объекта, вы можете выбрать между:
Вы можете сделать что-то вроде следующего. В этом случае вы используете блокировку на a и b для синхронизации вместо блокировки на "this". Мы не можем использовать int, потому что примитивные значения не имеют блокировок, поэтому мы используем Integer.
class x{
private Integer a;
private Integer b;
public void addA(){
synchronized(a) {
a++;
}
}
public synchronized void addB(){
synchronized(b) {
b++;
}
}
}