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

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

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

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

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

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

Ответ 1

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

Ответ 2

В примере 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 часто ссылается на эту сущность просто как на "монитор".) Внутренние блокировки играют роль в обоих аспектах синхронизации: обеспечение исключительного доступа к состоянию объекта и установление отношений "до появления", которые важны для видимости.

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

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

Ответ 3

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

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

Ответ 4

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

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

совпадает с:

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

class A {

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

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

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

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

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

Ответ 10

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

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

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();
    }
}
}

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

Ouput с noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects() прокомментировал -the Выход в порядке Methoda в> Methoda Выход.. methodB в> methodB Выход Ouput with *noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects()* commented

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

Ouput with *synchronizedEffectiveAsMethodsCalledOnSameObject()* commented

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

Ответ 11

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

Ответ 12

Что происходит, если два разных объекта запускают два синхронизированных метода, и эти два метода обновляют одну и ту же переменную?

Ответ 13

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