¿Es seguro llamar a un método sincronizado desde otro método sincronizado?

81

Si un método sincronizado llama a otro método sincronizado, ¿es seguro para subprocesos?

void synchronized method1() {
     method2()
}

void synchronized method2() {
}
usuario705414
fuente
¿Este artículo ayudaría a responder, o dónde estás confundido? kalyanchakravarthy.net/?p=413
James Black
Sí, en realidad no es necesario marcar el método2 como sincronizado asumiendo que solo se llama en el contexto dado anteriormente.
debracey
4
Además, si es seguro para subprocesos dependerá de lo que suceda en los dos métodos. Si llaman a listas no seguras para subprocesos, por ejemplo, es posible que no sean seguras para subprocesos, si algún otro subproceso pudiera modificar esa colección.
James Black
Como respuesta a lo que supongo, es la pregunta real: sí, la palabra clave sincronizada usa bloqueos recursivos; puede llamar de forma segura a un método sincronizado desde otro método sincronizado.
Brett Kail
Ha pasado un tiempo, pero sigue siendo el primer éxito en Google, así que: Sí, los bloques / métodos sincronizados en el mismo objeto son reentrantes. stackoverflow.com/questions/12219376/reentrant-synchronization
Szocske

Respuestas:

103

Sí, cuando marca métodos como synchronized, realmente está haciendo esto:

void method1() {
    synchronized (this) {
        method2()
    }
}

void method2() {
    synchronized (this) {
    }
}

Cuando la llamada al hilo ingresa al método2 desde el método1, entonces se asegurará de que mantiene el bloqueo this, lo cual ya lo hará, y luego puede pasar.

Cuando el hilo entra directamente en el método1 o el método2, entonces se bloqueará hasta que pueda obtener el bloqueo ( this), y luego entrará.

Como señaló James Black en los comentarios, debe ser consciente de lo que hace dentro del cuerpo del método.

private final List<T> data = new ArrayList<T>();

public synchronized void method1() {
    for (T item : data) {
        // ..
    }
}

public void method3() {
    data.clear();
}

De repente, no es seguro para subprocesos porque está mirando a ConcurrentModificationExceptionen su futuro porque method3no está sincronizado y, por lo tanto, podría ser llamado por Thread A mientras Thread B está funcionando method1.

pickypg
fuente
Estoy tratando de responder una pregunta casi idéntica a la que se hace aquí. Estas son las 2 posibles respuestas (las otras 2 dicen que no funcionará), ¿cuál es la correcta? C. El código se ejecutará pero existe una situación de punto muerto potencial D. El código funcionará bien ya que Java proporciona una sincronización reentrante que permite que un hilo adquiera el mismo bloqueo más de una vez ----- Supongo que es D, pero tal vez ¿La posible situación de bloqueo depende del cuerpo del método?
@ user3140993 El código aquí no tiene posibilidad de un interbloqueo. method3muestra operaciones de subprocesamiento inseguras, pero estás acertado sobre la sincronización reentrante.
pickypg
7

Es un método marcado con llamada sincronizada, otro método sincronizado seguro para subprocesos.

En general, no es posible decirlo. Depende de lo que hagan los métodos y de lo que hagan otros métodos en la misma y en otras clases.

Sin embargo, podemos estar seguros de que las llamadas al método1 y al método2 en el mismo objeto realizadas por diferentes subprocesos no se ejecutarán simultáneamente. Dependiendo de lo que hagan los métodos, esto puede ser suficiente para decir que la clase es segura para subprocesos con respecto a estos métodos.

Stephen C
fuente
2

Desde el sitio de tutoriales de Java http://download.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

  1. No es posible que se intercalen 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 objetos (suspenden la ejecución) hasta que el primer subproceso finaliza con el objeto.

  2. cuando un método sincronizado sale, automáticamente establece una relación de suceder antes 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.

Por lo tanto, Java se asegurará de que si 2 subprocesos están ejecutando el mismo método, los métodos no se ejecutarán de forma simultánea, sino uno tras otro.

Pero debe ser consciente del problema de Liveness, http://download.oracle.com/javase/tutorial/essential/concurrency/starvelive.html

Y también si está bloqueando innecesariamente, porque en el código que usó esto , que bloquea todo el objeto, si su objeto solo necesita acceso de sincronización a una variable, debe bloquear esa variable.

Stephen Lee
fuente
@Stephen Lee: no puede bloquear una variable. Entonces dices synchronized (this.someVar)que estás mirando el objeto cuya referencia se mantiene someVar. La distinción es muy importante.
Stephen C