Si sincronicé dos métodos en la misma clase, ¿pueden ejecutarse simultáneamente?

164

Si sincronicé dos métodos en la misma clase, ¿pueden ejecutarse simultáneamente en el mismo objeto ? por ejemplo:

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

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

Sé que no puedo ejecutar methodA()dos veces en el mismo objeto en dos hilos diferentes. lo mismo en methodB().

¿Pero puedo ejecutar methodB()en un hilo diferente mientras methodA()todavía se está ejecutando? (mismo objeto)

Shelef
fuente

Respuestas:

148

Ambos métodos bloquean el mismo monitor. Por lo tanto, no puede ejecutarlos simultáneamente en el mismo objeto desde diferentes subprocesos (uno de los dos métodos se bloqueará hasta que el otro haya terminado).

NPE
fuente
1
Tenía un complemento para esta pregunta. Supongamos que ambos métodos son estáticos, ahora se llama al método A usando Class, mientras que al método B se le llama usando un objeto como A.methodA () en t1 y obj.methodB () en t2. ¿Qué pasará ahora, bloquearán?
amod
2
@ amod0017: obj.methodB()es sinónimo de A.methodB()cuándo methodB()es static. Por lo tanto, sí, bloquearán (en el monitor de la clase, no del objeto).
NPE
intentará volver a ello. :)
amod
@NPE Entonces, incluso si ambos métodos son estáticos y 2 subprocesos t1 y t2 en el mismo objeto intentan llamar al método A () y al método B () simultáneamente, solo se ejecutará 1 (digamos t1) y el otro subproceso debe esperar hasta que t1 libere el bloqueo ?
sreeprasad
8
Tenga en cuenta que los métodos estáticos usan bloqueo en el .classobjeto. Entonces si tienes class A {static synchronized void m() {} }. Y luego un hilo lo llama new A().m()adquiere bloqueo en el new A()objeto. Si luego otro hilo lo llama A.m(), ENTRA AL MÉTODO SIN PROBLEMA porque lo que busca es bloquear el A.classobjeto mientras NO HILOS poseen este tipo de bloqueo . Entonces, a pesar de que declaró el método, synchronizeden realidad ES ACCEDIDO por dos hilos diferentes AL MISMO TIEMPO . Por lo tanto: nunca use referencias de objetos para llamar a métodos estáticos
Alex Semeniuk
113

En el ejemplo, el método A y el método B son métodos de instancia (a diferencia de los métodos estáticos). Poner synchronizedun método de instancia significa que el hilo debe adquirir el bloqueo (el "bloqueo intrínseco") en la instancia del objeto al que se llama el método antes de que el hilo pueda comenzar a ejecutar cualquier código en ese método.

Si tiene dos métodos de instancia diferentes marcados como sincronizados y diferentes subprocesos llaman a esos métodos simultáneamente en el mismo objeto, esos subprocesos competirán por el mismo bloqueo. Una vez que un subproceso obtiene el bloqueo, todos los demás subprocesos quedan excluidos de todos los métodos de instancia sincronizados en ese objeto.

Para que los dos métodos se ejecuten simultáneamente, tendrían que usar diferentes bloqueos, como este:

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
        }
    }
}

donde la sintaxis del bloque sincronizado permite especificar un objeto específico en el que el subproceso en ejecución necesita adquirir el bloqueo intrínseco para ingresar al bloque.

Lo importante a entender es que a pesar de que estamos poniendo una palabra clave "sincronizada" en métodos individuales, el concepto central es el bloqueo intrínseco detrás de escena.

Así es como el tutorial de Java describe la relación:

La sincronización se basa en una entidad interna conocida como bloqueo intrínseco o bloqueo de monitor. (La especificación de API a menudo se refiere a esta entidad simplemente como un "monitor".) Los bloqueos intrínsecos juegan un papel en ambos aspectos de la sincronización: imponer el acceso exclusivo al estado de un objeto y establecer relaciones antes de que sean esenciales para la visibilidad.

Cada objeto tiene un bloqueo intrínseco asociado. Por convención, un hilo que necesita acceso exclusivo y consistente a los campos de un objeto tiene que adquirir el bloqueo intrínseco del objeto antes de acceder a ellos, y luego liberar el bloqueo intrínseco cuando se hace con ellos. Se dice que un subproceso posee el bloqueo intrínseco entre el momento en que adquirió el bloqueo y lo liberó. Mientras un hilo posea un bloqueo intrínseco, ningún otro hilo puede adquirir el mismo bloqueo. El otro hilo se bloqueará cuando intente adquirir el bloqueo.

El propósito del bloqueo es proteger los datos compartidos. Usaría bloqueos separados como se muestra en el código de ejemplo anterior solo si cada bloqueo protegía a diferentes miembros de datos.

Nathan Hughes
fuente
entonces, en este ejemplo, el bloqueo está en los objetos lockA \ lockB y no en la clase A? ¿Es este un ejemplo de bloqueo de nivel de clase ?
Nimrod
2
@Nimrod: se bloquea en los objetos lockA y lockB y no en la instancia de A. nada aquí se bloquea en una clase. el bloqueo a nivel de clase significaría obtener el bloqueo en un objeto de clase, usando algo como static synchronizedosynchronized (A.class)
Nathan Hughes
Aquí está el enlace al tutorial de Java que explica exactamente lo que se responde aquí.
Alberto de Paola
18

Java Thread adquiere un bloqueo de nivel de objeto cuando ingresa en un método Java sincronizado de instancia y adquiere un bloqueo de nivel de clase cuando ingresa en un método Java sincronizado estático .

En su caso, los métodos (instancia) son de la misma clase. Entonces, cuando un hilo ingresa al método o bloque sincronizado de Java, adquiere un bloqueo (el objeto en el que se llama el método). Por lo tanto, no se puede llamar a otro método al mismo tiempo en el mismo objeto hasta que se complete el primer método y se libere el bloqueo (en el objeto).

Srikanth
fuente
Si tengo dos hilos en dos instancias diferentes de la clase, entonces podrán ejecutar ambos métodos simultáneamente, de modo que un hilo llama a un método sincronizado y el otro llama al segundo método sincronizado. Si mi comprensión es correcta, ¿puedo usar private final Object lock = new object();con sincronizado para permitir que solo un hilo pueda ejecutar cualquiera de los métodos? Gracias
Yug Singh
13

En su caso, sincronizó dos métodos en la misma instancia de clase. Por lo tanto, estos dos métodos no pueden ejecutarse simultáneamente en diferentes subprocesos de la misma instancia de clase A. Pero sí pueden en diferentes instancias de clase A.

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

es lo mismo que:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}
Oleksandr_DJ
fuente
¿Qué pasa si defino un bloqueo como private final Object lock = new Object();y ahora lo uso lockcon bloque sincronizado en dos métodos, entonces su declaración será cierta? En mi opinión, Object es la clase principal de todos los objetos, por lo que incluso si los subprocesos están en una instancia diferente de la clase, solo uno puede acceder al código dentro del bloque sincronizado a la vez. Gracias.
Yug Singh
Si define "bloqueo de objeto final privado" en la clase y se sincroniza con él, debe completar el bloqueo por instancia de clase, por lo que se comportará igual que sincronizado (esto).
Oleksandr_DJ
Sí, Object es un elemento primario para todas las clases, pero la instancia de "bloqueo" en su caso es "instancia por clase propietaria", por lo tanto, tiene el mismo efecto que "this" para la sincronización.
Oleksandr_DJ
7

Desde el enlace de documentación de Oracle

Hacer métodos sincronizados tiene dos efectos:

Primero, no es posible intercalar dos invocaciones de métodos sincronizados en el mismo objeto. Cuando un subproceso está ejecutando un método sincronizado para un objeto, todos los demás subprocesos que invocan métodos sincronizados para el mismo bloque de objeto (suspender la ejecución) hasta que el primer subproceso se realiza con el objeto.

En segundo lugar, cuando sale un método sincronizado, establece automáticamente una relación de suceso anterior con cualquier invocación posterior de un método sincronizado para el mismo objeto. Esto garantiza que los cambios en el estado del objeto sean visibles para todos los hilos

Esto responderá a su pregunta: en el mismo objeto, no puede llamar al segundo método sincronizado cuando se está ejecutando el primer método sincronizado.

Eche un vistazo a esta página de documentación para comprender los bloqueos intrínsecos y el comportamiento del bloqueo.

Aditya W
fuente
6

Piense en su código como el siguiente:

class A {

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

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

Entonces, sincronizado a nivel de método simplemente significa sincronizado (esto). si algún subproceso ejecuta un método de esta clase, obtendría el bloqueo antes de comenzar la ejecución y lo mantendría hasta que finalice la ejecución del método.

Pero, ¿puedo ejecutar methodB () en un hilo diferente mientras methodA () todavía se está ejecutando? (mismo objeto)

De hecho, no es posible!

Por lo tanto, varios subprocesos no podrán ejecutar ningún número de métodos sincronizados en el mismo objeto simultáneamente.

Khosro Makari
fuente
¿Qué pasa si creo hilos en dos objetos diferentes de la misma clase? En este caso, si llamo a un método desde un subproceso y otro método desde el segundo subproceso, ¿no se ejecutarán simultáneamente?
Yug Singh
2
Lo harán porque son objetos diferentes. Es decir, si desea evitarlo, puede usar métodos estáticos y sincronizar la clase o usar un objeto de variable de clase como un bloqueo o hacer que la clase sea Singleton. @Yug Singh
Khosro Makari
4

Para toda claridad, es posible que tanto el método sincronizado estático como el sincronizado no estático puedan ejecutarse de manera simultánea o simultánea porque uno tiene bloqueo de nivel de objeto y otro bloqueo de nivel de clase.

pacmanfordinner
fuente
3

La idea clave con la sincronización que no se hunde fácilmente es que tendrá efecto solo si se invocan métodos en la misma instancia de objeto (ya se ha resaltado en las respuestas y comentarios)

El siguiente programa de muestra es para señalar claramente lo mismo:

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

Observe la diferencia en la salida de cómo se permite el acceso simultáneo como se esperaba si se invocan métodos en diferentes instancias de objeto.

Ouput con noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () comentó: la salida está en orden methodA in> methodA Out .. methodB in> methodB Out Ouput con * noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () * comentó

y Ouput con synchronizedEffectiveAsMethodsCalledOnSameObject () comentado : la salida muestra el acceso simultáneo del método A por Thread1 y Thread0 en la sección resaltada.

Ouput con * synchronizedEffectiveAsMethodsCalledOnSameObject () * comentó

Aumentar el número de hilos lo hará aún más notable.

somshivam
fuente
2

No, no es posible, si fuera posible, ambos métodos podrían actualizar la misma variable simultáneamente, lo que podría dañar fácilmente los datos.

fastcodejava
fuente
2

Sí, pueden ejecutar simultáneamente ambos hilos. Si crea 2 objetos de la clase ya que cada objeto contiene solo un bloqueo y cada método sincronizado requiere bloqueo. Por lo tanto, si desea ejecutar simultáneamente, cree dos objetos y luego intente ejecutar utilizando esas referencias de objeto.

coolts
fuente
1

Lo está sincronizando en un objeto, no en una clase. Entonces no pueden correr simultáneamente en el mismo objeto

xyz
fuente
0

Dos subprocesos diferentes que ejecutan un método sincronizado común en el único objeto, ya que el objeto es el mismo, cuando un subproceso lo usa con el método sincronizado, tendrá que variar el bloqueo, si el bloqueo está habilitado, este subproceso irá al estado de espera, si el bloqueo está deshabilitado, puede acceder al objeto, mientras que accederá habilitará el bloqueo y lo liberará solo cuando se complete su ejecución. cuando llegue el otro subproceso, variará el bloqueo, ya que está habilitado esperará hasta que el primer subproceso complete su ejecución y libere el bloqueo puesto en el objeto, una vez que se libere el bloqueo, el segundo subproceso obtendrá acceso al objeto y habilitará el bloqueo hasta su ejecución. entonces la ejecución no será concurrente, ambos hilos se ejecutarán uno por uno,

Ankit yadav
fuente
1
Por favor puntúe y capitalice este desorden correctamente. No existe una palabra como 'varificar'.
Marqués de Lorne