Если я синхронизировал два метода в одном классе, могут ли они работать одновременно?

Если я синхронизировал два метода в одном классе, могут ли они работать одновременно на одном и том же объекте? Например:

class A {
    public synchronized void methodA() {
        //method A
    }

    public synchronized void methodB() {
        // method B
    }
}

Я знаю, что не могу запустить methodA() дважды на одном и том же объекте в двух разных потоках. то же самое в methodB().

Но могу ли я запустить methodB() в другом потоке, пока methodA() все еще работает? (тот же объект)


person Shelef    schedule 15.03.2013    source источник


Ответы (12)


Оба метода блокируют один и тот же монитор. Следовательно, вы не можете одновременно выполнять их для одного и того же объекта из разных потоков (один из двух методов будет заблокирован, пока другой не будет завершен).

person NPE    schedule 15.03.2013
comment
У меня было дополнение к этому вопросу. Предположим, что оба метода статичны, теперь методA вызывается с использованием класса, а методB вызывается с использованием объекта, такого как A.methodA () в t1 и obj.methodB () в t2. Что теперь будет, заблокируют ???? - person amod; 21.03.2013
comment
@ amod0017: obj.methodB() является синонимом A.methodB(), когда methodB() равно static. Следовательно, да, они будут блокироваться (на мониторе класса, а не объекта). - person NPE; 21.03.2013
comment
постараюсь вернуться к этому. :) - person amod; 21.03.2013
comment
@NPE Итак, даже если оба метода являются статическими и 2 потока t1 и t2 на одном объекте пытаются одновременно вызвать methodA () и methodB (), тогда будет выполняться только 1 поток (скажем, t1), а другой поток должен ждать, пока t1 не освободит блокировку ? - person sreeprasad; 04.05.2015
comment
Имейте в виду, что статические методы используют блокировку объекта .class. Итак, если у вас есть class A {static synchronized void m() {} }. Затем один поток вызывает new A().m() и получает блокировку new A() объекта. Если затем другой поток вызывает A.m(), он ВХОДИТ В МЕТОД БЕЗ ПРОБЛЕМЫ, потому что то, что он ищет, - это блокировка объекта A.class, в то время как НИКАКИЕ НИТИ не обладают такой блокировкой. Таким образом, даже несмотря на то, что вы объявили метод synchronized, на самом деле к нему получают доступ два разных потока ОДНОВРЕМЕННО. Таким образом: никогда не используйте ссылки на объекты для вызова статических методов - person Alex Semeniuk; 19.10.2015
comment
@AlexSemeniuk, если я использую private final Object lock = new Object();, а затем public void m1(){synchronized(lock){}} и public void m2(){synchronized(lock){}} и вызываю m1 и m2 из потоков t1 и t2, созданных на разных объектах, не смогут ли t1 и t2 получить доступ к m1 и m2 одновременно? ИМО их быть не должно. Пожалуйста, поправьте меня, если я ошибаюсь. Спасибо. - person Yug Singh; 22.10.2018
comment
Да, он будет синхронизирован, поскольку вы используете один и тот же объект синхронизации. Мой комментарий был связан с особенностями использования ключевого слова synchronized в объявлениях методов. - person Alex Semeniuk; 24.10.2018

В примере methodA и methodB - это методы экземпляра (в отличие от статических методов). Помещение synchronized в метод экземпляра означает, что поток должен получить блокировку (внутреннюю блокировку) на экземпляре объекта, для которого вызывается метод, прежде чем поток сможет начать выполнение любого кода в этом методе.

Если у вас есть два разных метода экземпляра, помеченных как синхронизированные, и разные потоки одновременно вызывают эти методы для одного и того же объекта, эти потоки будут бороться за одну и ту же блокировку. Как только один поток получает блокировку, все другие потоки закрываются для всех синхронизированных методов экземпляра этого объекта.

Чтобы два метода работали одновременно, им пришлось бы использовать разные блокировки, например:

class A {
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void methodA() {
        synchronized(lockA) {
            //method A
        }
    }

    public void methodB() {
        synchronized(lockB) {
            //method B
        }
    }
}

где синтаксис синхронизированного блока позволяет указать конкретный объект, который исполняющему потоку необходимо получить внутреннюю блокировку, чтобы войти в блок.

Важно понимать, что даже несмотря на то, что мы помещаем ключевое слово synchronized в отдельные методы, основная концепция заключается в скрытой внутренней блокировке.

Вот как руководство по Java описывает отношения:

Синхронизация строится вокруг внутренней сущности, известной как внутренняя блокировка или блокировка монитора. (Спецификация API часто называет этот объект просто монитором.) Внутренние блокировки играют роль в обоих аспектах синхронизации: обеспечение монопольного доступа к состоянию объекта и установление отношений «происходит раньше», которые необходимы для видимости.

С каждым объектом связана внутренняя блокировка. По соглашению поток, которому требуется монопольный и согласованный доступ к полям объекта, должен получить внутреннюю блокировку объекта перед доступом к ним, а затем освободить внутреннюю блокировку, когда это будет сделано с ними. Говорят, что поток владеет внутренней блокировкой между моментом, когда он получил блокировку и снял блокировку. Пока поток владеет внутренней блокировкой, никакой другой поток не может получить такую ​​же блокировку. Другой поток заблокируется, когда попытается получить блокировку.

Блокировка предназначена для защиты общих данных. Вы должны использовать отдельные блокировки, как показано в приведенном выше примере кода, только если каждая блокировка защищает разные элементы данных.

person Nathan Hughes    schedule 15.03.2013
comment
Итак, в этом примере блокировка находится на объектах lockA \ lockB, а не на классе A? Это пример блокировки на уровне класса? - person Nimrod; 21.07.2016
comment
@Nimrod: он блокируется на объектах lockA и lockB, а не на экземпляре A. Здесь ничего не блокируется для класса. Блокировка на уровне класса будет означать получение блокировки на объекте класса, используя что-то вроде static synchronized или synchronized (A.class) - person Nathan Hughes; 21.07.2016
comment
Вот ссылка на учебное пособие по java, объясняющее, что именно ответил здесь. - person Alberto de Paola; 09.11.2016

Java Thread получает блокировку уровня объекта, когда он входит в экземпляр синхронизированного java-метода и получает блокировку уровня класса когда он входит в статический синхронизированный Java-метод.

В вашем случае методы (экземпляр) относятся к одному классу. Поэтому, когда когда-либо поток входит в синхронизированный метод Java или блокируется, он получает блокировку (объект, для которого вызывается метод). Таким образом, другой метод не может быть вызван одновременно для одного и того же объекта, пока первый метод не будет завершен и блокировка (для объекта) не будет снята.

person Srikanth    schedule 15.03.2013
comment
если у меня есть два потока в двух разных экземплярах класса, они смогут выполнять оба метода одновременно, так что один поток вызывает один синхронизированный метод, а другой - второй синхронизированный метод. Если я правильно понимаю, могу ли я использовать private final Object lock = new object(); с synchronized, чтобы только один поток мог выполнять любой из методов? Спасибо - person Yug Singh; 22.10.2018

В вашем случае вы синхронизировали два метода в одном экземпляре класса. Итак, эти два метода не могут работать одновременно в разных потоках одного и того же экземпляра класса A. Но они могут работать в разных экземплярах класса A.

class A {
    public synchronized void methodA() {
        //method A
    }
}

такой же как:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}
person Oleksandr_DJ    schedule 15.03.2013
comment
что, если я определю блокировку как private final Object lock = new Object(); и теперь использую lock с синхронизированным блоком двумя методами, тогда будет ли ваше утверждение истинным? IMO, поскольку Object является родительским классом для всех объектов, поэтому, даже если потоки находятся в другом экземпляре класса, только один может получить доступ к коду внутри синхронизированного блока за раз. Спасибо. - person Yug Singh; 22.10.2018
comment
Если вы определяете частную финальную блокировку объекта в классе и синхронизируете с ним, вы заполняете блокировку для каждого экземпляра класса, поэтому он будет вести себя так же, как синхронизированный (this). - person Oleksandr_DJ; 08.11.2018
comment
Да, Object является родительским для всех классов, но экземпляр блокировки в вашем случае является экземпляром для каждого класса-владельца, поэтому он имеет тот же эффект, что и для синхронизации. - person Oleksandr_DJ; 08.11.2018

Думайте о своем коде как о следующем:

class A {

public void methodA() {
    synchronized(this){        
      //method A body
    }
}

public void methodB() {
    synchronized(this){
      // method B body
    }
}

Итак, синхронизированный на уровне метода просто означает синхронизированный (это). если какой-либо поток запускает метод этого класса, он получит блокировку перед запуском выполнения и удержит ее до завершения выполнения метода.

Но могу ли я запустить methodB () в другом потоке, пока methodA () все еще работает? (тот же объект)

Действительно, это невозможно!

Следовательно, несколько потоков не смогут запускать любое количество синхронизированных методов для одного и того же объекта одновременно.

person Khosro Makari    schedule 05.01.2018
comment
что, если я создам потоки на двух разных объектах одного и того же класса? В этом случае, если я вызову один метод из одного потока, а другой метод из второго, не будут ли они выполняться одновременно? - person Yug Singh; 22.10.2018
comment
Они будут, потому что это разные объекты. То есть, если вы хотите предотвратить это, вы можете использовать статические методы и синхронизировать класс или использовать объект переменной класса в качестве блокировки или сделать класс Singleton. @Yug Singh - person Khosro Makari; 07.11.2018

Из документации Oracle ссылка

Синхронизация методов имеет два эффекта:

Во-первых, невозможно чередование двух вызовов синхронизированных методов для одного и того же объекта. Когда один поток выполняет синхронизированный метод для объекта, все другие потоки, вызывающие синхронизированные методы для того же блока объекта (приостанавливают выполнение), пока первый поток не завершит работу с объектом.

Во-вторых, когда синхронизированный метод завершается, он автоматически устанавливает связь «произошло до» с любым последующим вызовом синхронизированного метода для того же объекта. Это гарантирует, что изменения состояния объекта видны всем потокам.

Это ответит на ваш вопрос: для того же объекта вы не можете вызвать второй синхронизированный метод, когда выполняется первый синхронизированный метод.

Ознакомьтесь с страницей в этой документации, чтобы понять внутренние блокировки и блокировку. поведение.

person Aditya W    schedule 21.02.2016

Для полной ясности, вполне возможно, что как статический синхронизированный, так и нестатический синхронизированный метод могут работать одновременно или одновременно, потому что у одного есть блокировка на уровне объекта, а у другого - на уровне класса.

person pacmanfordinner    schedule 13.07.2014

Ключевая идея синхронизации, которую нелегко понять, заключается в том, что она будет иметь эффект только в том случае, если методы вызываются для одного и того же экземпляра объекта - это уже было выделено в ответах. и комментарии -

Ниже приведен пример программы, чтобы четко определить то же самое -

public class Test {

public synchronized void methodA(String currentObjectName) throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA out");
}

public synchronized void methodB(String currentObjectName)  throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB out");
}

public static void main(String[] args){
    Test object1 = new Test();
    Test object2 = new Test();
    //passing object instances to the runnable to make calls later
    TestRunner runner = new TestRunner(object1,object2);
    // you need to start atleast two threads to properly see the behaviour
    Thread thread1 = new Thread(runner);
    thread1.start();
    Thread thread2 = new Thread(runner);
    thread2.start();
}
}

class TestRunner implements Runnable {
Test object1;
Test object2;

public TestRunner(Test h1,Test h2) {
    this.object1 = h1;
    this.object2 = h2;
}

@Override
public void run() {
    synchronizedEffectiveAsMethodsCalledOnSameObject(object1);
    //noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(object1,object2);
}

// this method calls the method A and B with same object instance object1 hence simultaneous NOT possible
private void synchronizedEffectiveAsMethodsCalledOnSameObject(Test object1) {
    try {
        object1.methodA("object1");
        object1.methodB("object1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// this method calls the method A and B with different object instances object1 and object2 hence simultaneous IS possible
private void noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(Test object1,Test object2) {
    try {
        object1.methodA("object1");
        object2.methodB("object2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

Обратите внимание на разницу в выводе того, как разрешен одновременный доступ как ожидалось, если методы вызываются для разных экземпляров объекта.

Результат с noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () с комментариями - вывод находится в порядке methodA in> methodA Out .. methodB in> methodB Out  Результат с комментариями * noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () *

и Ouput с synchronizedEffectiveAsMethodsCalledOnSameObject () с комментариями - выходные данные показывают одновременный доступ к методу A со стороны Thread1 и Thread0 в выделенном разделе -

Результат с комментарием * synchronizedEffectiveAsMethodsCalledOnSameObject () *

Увеличение количества потоков сделает это еще более заметным.

person somshivam    schedule 08.04.2019

Нет, это невозможно, если бы это было возможно, тогда оба метода могли бы обновлять одну и ту же переменную одновременно, что могло бы легко повредить данные.

person fastcodejava    schedule 14.01.2019

Вы синхронизируете его с объектом, а не с классом. Таким образом, они не могут работать одновременно с одним и тем же объектом

person Ajinkya    schedule 15.03.2013

Да, они могут одновременно запускать оба потока. Если вы создаете 2 объекта класса, так как каждый объект содержит только одну блокировку, а каждый синхронизированный метод требует блокировки. Поэтому, если вы хотите работать одновременно, создайте два объекта, а затем попробуйте запустить их, используя ссылку на этот объект.

person coolts    schedule 23.05.2019
comment
Я почти уверен, что они имели в виду, если оба метода вызываются для одного и того же объекта - person parsecer; 17.11.2020

Два разных потока, выполняющих общий синхронизированный метод для одного объекта, поскольку объект такой же, когда один поток использует его с синхронизированным методом, он должен будет проверить блокировку, если блокировка включена, этот поток перейдет в состояние ожидания, если блокировка отключена, он может получить доступ к объекту, в то время как он получит доступ, он включит блокировку и снимет блокировку только после завершения его выполнения. когда прибудут другие потоки, он проверит блокировку, поскольку он включен, он будет ждать, пока первый поток завершит свое выполнение и освободит блокировку, установленную на объекте, как только блокировка будет снята, второй поток получит доступ к объекту и он включит блокировку до тех пор, пока не будет выполнен. поэтому выполнение не будет параллельным, оба потока будут выполняться один за другим, когда оба потока используют синхронизированный метод для разных объектов, они будут выполняться одновременно.

person Ankit yadav    schedule 07.02.2017
comment
Пожалуйста, расставьте акценты и используйте заглавные буквы в этом беспорядке. Нет такого слова, как «варить». - person user207421; 05.01.2018