2010-06-26 18 views
6

Soy nuevo en multiprocesamiento, y escribí este código que imprime los números del 1 al 10000 al hacer que los subprocesos en ejecución incrementen e impriman una variable.¿Está 'sincronizado' realmente solo el azúcar sintáctico?

Aquí está el código que estoy usando:

package threadtest; 

public class Main{ 

    static int i=0; 
    static Object lock=new Object(); 

    private static class Incrementer extends Thread{ 

     @Override 
     public void run(){ 
      while (true){ 
       synchronized(lock){ 
         if (i>=10000) 
          break; 
         i++; 
         System.out.println(i); 
       } 
      }    
     } 
    } 


    public static void main(String[] args) { 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
    } 
} 

Esto funciona - escribí un programa de prueba para comprobar la salida, y los números impresos son exactamente 1-10000 en orden.

Mi pregunta es esta: he escuchado que synchronized es solo azúcar sintáctica. Pero parece que no puedo lograr un resultado exitoso sin usarlo. ¿Qué me estoy perdiendo?

+6

Ciertamente no lo es. ¿Puedes citar las fuentes donde has escuchado esto? – BalusC

+1

@BalusC: ver la respuesta aceptada. Creo que las fuentes simplemente se estaban refiriendo a los * métodos * sincronizados como azúcar sintáctico. No estoy seguro de si eso es exactamente correcto o no, pero al menos no contradice mis hallazgos, como pensé que era. @skaffman: podría haberlo hecho mal sin 'sincronizar' por todo lo que sabía :) – Cam

+0

@incrediman' synchronized' en un método es más o menos lo mismo que poner el cuerpo del método en 'sincronizado (esto) {...} '(o para un método estático' sincronizado (MyClass.class) {...} '). (Se representa de forma diferente en los archivos de clase, pero eso no debería preocuparlo). –

Respuesta

14

synchronized de ninguna manera es azúcar sintáctica para nada. No hay forma de trabajar bloqueos en Java sin utilizar la palabra clave synchronized.

Donde hay "azúcar sintáctico" de una clase en bloqueos en Java es que synchronized puede aplicarse tanto a bloques (como lo ha hecho anteriormente) como a métodos completos. Los dos métodos siguientes son aproximadamente equivalentes en la semántica:

synchronized void method1() { 
    // ... do stuff ... 
} 

void method2() { 
    synchronized(this) { 
    // ... do stuff ... 
    } 
} 

Así que por qué le gustaría hacer la segunda versión en lugar del primero?

  • Las invocaciones de métodos sincronizados son mucho más lentas que las antiguas invocaciones de métodos antiguos, por ejemplo, en un orden de magnitud. Si no se garantiza que su código sincronizado siempre se ejecute (digamos que está en un condicional), entonces probablemente no desee sincronizar todo el método.
  • Los métodos sincronizados mantienen bloqueos durante más tiempo que los bloques sincronizados (debido a todo el método de configuración/código de desmontaje). El segundo método anterior mantendrá el bloqueo por menos tiempo porque el tiempo invertido en configurar y derribar el marco de pila no se bloqueará.
  • Puede tener un control mucho más preciso sobre exactamente lo que está bloqueando si va con los bloques sincronizados.
  • (Cortesía de starblue) Los bloques sincronizados pueden usar objetos distintos de this para el bloqueo, lo que le proporciona una semántica de bloqueo más flexible.
+0

Gracias, eso tiene sentido. Me alegro de haber aclarado :) – Cam

+0

Con un bloque puede utilizar algún objeto privado como el bloqueo, que evita la interferencia del código externo. – starblue

+0

Buen punto. Añadiré eso a la respuesta, starblue. –

1

Parece que las fuentes son incorrectas. La palabra clave syncrhonized es importante para usar, y usar correctamente, al escribir código seguro para subprocesos. Y parece que tus propios experimentos lo corroboran.

Para más información sobre la sincronización en Java:

Java Synchronized Methods

Java Locks and Synchronized Statements

0

La sincronización es uno de los conceptos más importantes, mientras que la programación en entorno multi hilo. Al usar la sincronización, uno tiene que considerar el objeto sobre el que tiene lugar la sincronización. Por ejemplo, si un método estático es para ser sincronizado entonces la sincronización debe estar en el nivel de clase utilizando

synchronized(MyClass.class){ 
//code to be executed in the static context 
} 

si el bloque en método de instancia ha de ser sincronizado entonces la sincronización debe utilizar una instancia de un objeto que se comparte entre todos los hilos. La mayoría de las personas se equivocan con el segundo punto, ya que aparece en el código donde la sincronización parece estar en objetos diferentes en lugar de en un solo objeto.

1

En realidad a partir de Java 5 tiene (formalmente) un conjunto alternativo de herramientas en java.util.concurrent. Vea here para más detalles. Como se detalla en el artículo, el modelo de bloqueo del monitor provisto en el nivel de lenguaje de Java tiene varias limitaciones significativas y puede ser difícil razonar cuando hay un conjunto complejo de objetos interdependientes y relaciones de bloqueo que hacen que Live-lock sea una posibilidad real. La biblioteca java.util.concurrent ofrece una semántica de bloqueo que podría ser más familiar para los programadores que han tenido experiencia en sistemas similares a POSIX

1

Por supuesto, "sincronizado" es solo azúcar sintáctico: increíblemente útil azúcar sintáctica.

Si quieres programas Java sin azúcar, se debe escribir directamente en el código de bytes de Java del MonitorEnter, MonitorExit, bloqueo y desbloqueo operaciones referencia en VM Specifications 8.13 Locks and Synchronization

Hay es un bloqueo asociado con cada objeto. El lenguaje de programación Java no proporciona una forma de realizar operaciones de bloqueo y desbloqueo por separado ; en su lugar, son implícitamente realizadas por construcciones de alto nivel que siempre organizan el emparejamiento de tales operaciones correctamente. (La máquina virtual Java , sin embargo, proporciona separadas MonitorEnter y MonitorExit instrucciones que implementan el bloqueo y desbloquear operaciones.)

La declaración sincronizado calcula una referencia a un objeto; entonces intenta realizar una operación de bloqueo en ese objeto y no continúa hasta que la operación de bloqueo haya finalizado con éxito . (A operación de bloqueo puede retrasarse porque los reglas acerca de cerraduras puede evitar que el principal memoria de participar hasta que algún otro hilo está listo para realizar una o más operaciones de desbloqueo.) Después de la operación bloqueo ha sido realizado, la el cuerpo de la instrucción sincronizada es ejecutado. Normalmente, un compilador para el lenguaje programación Java asegura que la operación de bloqueo implementado por una instrucción MonitorEnter ejecutado antes de la ejecución del cuerpo de la declaración sincronizado está adaptada por una operación de desbloqueo implementado por una instrucción MonitorExit siempre la instrucción sincronizada completa, si la finalización es normal o abrupta.

Un método sincronizado automáticamente realiza una operación de bloqueo cuando se invoca ; su cuerpo no se ejecuta hasta que la operación de bloqueo haya finalizado con éxito . Si el método es un método de instancia, se bloquea la cerradura asociado a la instancia para la que se invocó (es decir, el objeto que se conoce como este durante la ejecución del cuerpo del método ). Si el método es estático, bloquea el bloqueo asociado con el objeto de clase que representa la clase en la que se define el método. Si la ejecución del cuerpo del método ha sido completa, ya sea normalmente o abruptamente, una operación de desbloqueo es ejecutada automáticamente en ese mismo bloqueo .

La mejor práctica es que si una variable es en ser asignada por un hilo y utilizado o asignado por la otra, entonces todo accesos a esa variable debe ser encerrado en métodos sincronizados o declaraciones sincronizados.

Aunque un compilador para el lenguaje de programación Java normalmente garantías estructurada uso de cerraduras (véase la Sección 7.14, "sincronización"), no existe seguridad de que todo el código sometido a la máquina virtual de Java oído a este propiedad. Las implementaciones de la máquina Java virtual están permitidas pero no son necesarias para hacer cumplir las dos reglas siguientes que garantizan el bloqueo estructurado.

Deje que T sea un hilo y L sea un candado. Entonces:

  1. El número de operaciones de bloqueo realizadas por T en L durante una invocación de método debe ser igual al número de desbloquear operaciones realizadas por T en L durante la invocación del método si la invocación del método completa normalmente o abruptamente.

  2. En ningún momento durante una invocación de método puede el número de operaciones de desbloqueo realizadas por T en L desde el método de invocación exceder el número de las operaciones de bloqueo realizado por T en L desde la invocación del método.

En términos menos formales, durante un método invocación de cada operación de desbloqueo en L debe coincidir con alguna operación precedente bloqueo sobre L.

Tenga en cuenta que el bloqueo y desbloqueo automáticamente realizado por el Java la máquina virtual cuando se invoca un método sincronizado se considera que ocurre durante la invocación del método de llamada .

Cuestiones relacionadas