2010-06-26 6 views
6

Je suis novice en multithreading, et j'ai écrit ce code qui imprime les nombres 1-10000 en ayant des threads en cours d'exécution incrémenter et imprimer une variable.Est-ce que 'synchronisé' est vraiment du sucre syntaxique?

Voici le code que je utilise:

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

Cela fonctionne - je l'ai écrit un programme de test pour vérifier la sortie et les numéros imprimés sont exactement 1-10000 dans l'ordre.

Ma question est la suivante: J'ai entendu dire que synchronized est seulement du sucre syntaxique. Mais je n'arrive pas à atteindre un résultat réussi sans l'utiliser. Qu'est-ce que je rate?

+6

Ce n'est certainement pas le cas. Pouvez-vous citer les sources où vous avez entendu cela? – BalusC

+1

@BalusC: Voir la réponse acceptée. Je pense que les sources se référaient simplement aux méthodes synchronisées * comme sucre syntaxique - je ne suis pas sûr que ce soit exact ou pas, mais au moins cela ne contredit pas mes conclusions comme je le pensais. @skaffman: J'aurais pu mal le faire sans 'synchronized' pour tout ce que je savais :) – Cam

+0

@incrediman' synchronized' sur une méthode est plus ou moins la même chose que mettre le corps de la méthode dans 'synchronized (this) {...} '(ou pour une méthode statique' synchronized (MyClass.class) {...} '). (Il est représenté différemment dans les fichiers de classe, mais cela ne devrait normalement pas vous inquiéter.) –

Répondre

14

synchronized n'est en aucun cas du sucre syntaxique pour quoi que ce soit. Il n'y a aucun moyen de travailler des verrous dans Java sans utiliser le mot-clé synchronized. Lorsqu'il y a du "sucre syntaxique" d'un tri dans les verrous en Java, synchronized peut s'appliquer à la fois aux blocs (comme vous l'avez fait ci-dessus) et aux méthodes entières. Les deux méthodes suivantes sont à peu près équivalent dans la sémantique:

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

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

Alors, pourquoi voudriez-vous faire la deuxième version au lieu de la première?

  • Les invocations de méthodes synchronisées sont beaucoup plus lentes que les anciennes invocations de méthodes simples, par exemple d'un ordre de grandeur. Si votre code synchronisé n'est pas garanti pour toujours s'exécuter (disons qu'il est dans un conditionnel), alors vous ne voulez probablement pas synchroniser la méthode entière.
  • Les méthodes synchronisées contiennent des verrous pour des blocs plus longs que les blocs synchronisés (à cause de tout le code de configuration/démontage de la méthode). La deuxième méthode ci-dessus tiendra le verrou pour moins de temps car le temps passé à mettre en place et à déchirer le cadre de la pile ne sera pas verrouillé.
  • Vous pouvez avoir un contrôle beaucoup plus précis sur ce que vous verrouillez si vous utilisez les blocs synchronisés.
  • (Courtesy of starblue) Les blocs synchronisés peuvent utiliser des objets autres que this pour le verrouillage, ce qui vous donne une sémantique de verrouillage plus flexible.
+0

Merci, cela a du sens. Heureux d'avoir cela éclairci :) – Cam

+0

Avec un bloc, vous pouvez utiliser un objet privé comme le verrou, ce qui empêche les interférences du code externe. – starblue

+0

Bon point. Je vais ajouter cela à la réponse, starblue. –

1

Il semble que vos sources soient fausses. Le mot-clé syncrhonized est important à utiliser - et à utiliser correctement - lors de l'écriture de code thread-safe. Et il semble que vos propres expériences le prouvent.

Pour en savoir plus sur la synchronisation en Java:

Java Synchronized Methods

Java Locks and Synchronized Statements

0

La synchronisation est l'un des concepts les plus importants alors que la programmation dans un environnement multi thread. Lors de l'utilisation de la synchronisation, il faut considérer l'objet sur lequel la synchronisation a lieu. Par exemple, si une méthode statique à synchroniser ensuite la synchronisation doit être au niveau de la classe en utilisant

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

si le bloc de méthode d'instance doit être synchronisé puis la synchronisation doit utiliser une instance d'un objet est partagé entre tous les threads. La plupart des gens se trompent avec le deuxième point tel qu'il apparaît dans votre code où la synchronisation semble se trouver sur des objets différents plutôt que sur un seul objet.

1

En fait, à partir de Java 5 vous avez (formellement) un ensemble d'outils alternatif dans java.util.concurrent. Voir here pour plus de détails. Comme détaillé dans l'article, le modèle de verrouillage de moniteur fourni au niveau de langage de Java comporte un certain nombre de limitations importantes et peut être difficile à raisonner lorsqu'il existe un ensemble complexe d'objets interdépendants et de relations de verrouillage. La bibliothèque java.util.concurrent offre une sémantique de verrouillage qui pourrait être plus familière aux programmeurs qui ont de l'expérience dans les systèmes POSIX

1

Bien sûr, "synchronisé" n'est que du sucre syntactique - un sucre syntaxique extrêmement utile.

Si vous voulez des programmes java sans sucre, vous devriez écrire directement dans le code octet java le monitorenter, monitorexit, verrouillage et déverrouillage opérations référencées dans VM Specifications 8.13 Locks and Synchronization

Il est un verrou associé à chaque objet. Le langage de programmation Java ne permet pas de d'effectuer des verrouillages séparés et de déverrouiller les opérations ; à la place, ils sont implicitement effectués par des constructions de haut niveau qui s'arrangent toujours pour coupler de telles opérations correctement. (La machine virtuelle Java , cependant, fournit des monitorenter et monitorexit séparées instructions qui mettent en oeuvre le verrou et déverrouiller les opérations.)

L'état synchronisé calcule une référence à un objet ; alors tente d'effectuer une opération de verrouillage sur cet objet et ne poursuit pas jusqu'à ce que l'opération de verrouillage ait terminé avec succès . (Un verrou opération peut être retardée parce que les règles sur les verrous peuvent empêcher la mémoire principale de participer jusqu'à ce qu'un autre thread est prêt à effectuer un ou plusieurs opérations de déverrouillage.) Après a été réalisée l'opération de verrouillage , le Le corps de l'instruction synchronisée est exécuté. Normalement, un compilateur pour le langage de programmation Java assure que l'opération de verrouillage mis en œuvre par une instruction de monitorenter exécuté avant l'exécution du corps de la déclaration synchronisée est adaptée par une opération de déverrouillage mis en œuvre par une instruction de monitorexit chaque fois l'instruction synchronisée se termine, si l'achèvement est normal ou brutal.Une méthode synchronisée exécute une opération de verrouillage lorsqu'elle est invoquée; son corps n'est pas exécuté jusqu'à ce que l'opération de verrouillage ait terminé . Si la méthode est une méthode d'instance, il verrouille la serrure associée à l'instance pour dont il a été invoqué (qui est l'objet qui sera connu sous le nom de cette lors de l'exécution du corps de la méthode). Si la méthode est statique, verrouille le verrou associé à l'objet de classe qui représente la classe dans laquelle la méthode est définie. Si l'exécution du corps de la méthode est achevée, normalement ou brusquement, une opération de déverrouillage est automatiquement effectuée sur ce même verrou .

Les meilleures pratiques est que si une variable est jamais être attribué par un fil et utilisés ou affectés par une autre, puis tous les accès à cette variable devrait être enfermé dans des méthodes synchronisées ou déclarations synchronisées.

Bien qu'un compilateur pour le langage de programmation Java normalement garantit une utilisation structurée des serrures (voir la section 7.14, « Synchronisation »), rien ne garantit que tout le code soumis à la machine virtuelle Java obéiront cette propriété. Les implémentations de la machine virtuelle Java sont autorisées mais non requises pour appliquer les deux règles suivantes garantissant le verrouillage structuré.

Soit T un filetage et L un verrou. Puis:

  1. Le nombre d'opérations de blocage effectuées par T sur L au cours d'une méthode appel doit être égal au nombre de débloquer les opérations effectuées par T sur L lors de l'appel de méthode si l'invocation de méthode se termine normalement ou brusquement.

  2. À aucun moment pendant un appel de méthode peut le nombre de déverrouillage opérations effectuées par T sur L depuis l'appel de méthode dépasse le nombre des opérations de verrouillage réalisée par T sur L depuis l'appel de méthode.

En termes moins formels, au cours d'une méthode invocation chaque opération de déverrouillage L doit correspondre à une opération verrouillage précédent sur L.

Notez que le verrouillage et le déverrouillage effectué automatiquement par le Java machine virtuelle lors de l'appel d'une méthode synchronisée sont considérés comme se produisent pendant l'appel de la méthode appelante.

Questions connexes