La raison pour laquelle une variable locale est finale ou effectivement définitive en raison de problèmes de concurrence. Dans la spécification jls 8, il indique ce qui suit:
La restriction aux variables finales effectives interdit l'accès à variables locales à changement dynamique, dont la capture serait probablement introduire des problèmes de concurrence.
Tout est bon et gentil, mais j'ai fait une petite expérience. Que se passe-t-il si je synchronise la méthode, cela éliminerait la possibilité de changer dynamiquement la variable locale car je suis assuré qu'un seul thread peut exécuter ce code. Mais la compilation a généré une erreur disant qu'elle doit être définitive ou effectivement définitive.
La logique est-elle correcte?
Considérez le code suivant:
public synchronized void capture() { int localVariable = 100; Interf i = (text) -> System.out.println(text + localVariable); i.m1("This local variable is: "); localVariable = 1000; } }
3 Réponses :
La réponse est simplement que votre variable sort du champ d'application à la fin de la méthode. Ceci est facilement résolu avec des variables finales efficaces car le compilateur copie simplement la valeur dans le lambda. Puisque le code de l'expression lambda peut également être exécuté en dehors de la méthode (où la variable modifiable est déjà garbage collecté), cela ne fonctionnera pas. Vous ne pouvez pas non plus vous attendre à ce que le compilateur fasse en quelque sorte une copie de votre variable, puis la modifie dynamiquement lorsqu'elle est modifiée en dehors de votre expression lambda. J'espère que cela clarifie les choses.
Mais la compilation a généré une erreur disant qu'elle doit être définitive ou effectivement définitive.
C'est parce qu'il respecte les règles. Non, pas de mais; peu importe si vous vous êtes réellement protégé contre tous les problèmes de concurrence ou non - si ce n'est pas effectivement définitif, il ne se compilera pas.
Dans votre exemple simpliste ici, c'est probablement correct. Cependant, la synchronisation de la méthode n'est pas pertinente car les variables locales seront toujours liées à leur appel par thread de toute façon. Ce sont des problèmes de thread dans le contexte de la méthode elle-même qui inquiètent le compilateur, et qui peuvent facilement se produire avec l'utilisation de lambdas (qui peuvent être exécutées à un moment arbitraire dans le futur, après que l'état d'une variable non finale ait changé. , et si c'est le cas, on ne sait pas du tout quel état doit être utilisé - l'état initial ou l'état mis à jour.)
Ce sont des problèmes de threading dans le contexte de la méthode elle-même qui inquiètent le compilateur
Je vois que c'est vrai, pouvez-vous me donner un exemple pour solidifier cela? De plus, le problème de thread que vous avez mentionné, est-ce que vous dites que si l'expression lambda peut modifier les variables locales, les autres threads n'obtiennent pas la visibilité parce que les locaux sont thread privés
Imaginez que le lambda que vous avez crée un CompletableFuture à exécuter par le ForkJoinPool ou un autre exécuteur?
C'est pourquoi la synchronisation sur cette méthode ne suffirait pas à annuler la règle de la finalisation effective de la variable locale. Le lambda s'exécutera de manière synchrone et sera synchronisé, mais pas la tâche asynchrone qu'il crée.
Êtes-vous sûr que la méthode à laquelle vous passez le lambda n'utilise pas un thread différent? Ou exécuter le lambda quelque temps plus tard? Considérez
SwingUtils.invokeLater (() -> System.out.println (localVariable))
. Que devrait imprimer cette impression?les règles sur final / effectivement final ne sont pas causées par la concurrence. C'est des causes par passage par valeur. Le lambda obtient une copie. Si la copie est modifiée dans le lambda ou à l'extérieur, les deux copies auront une valeur différente.
synchronized on method garantirait que la méthode est appelée par un seul thread. Mais cela ne garantit pas que la méthode elle-même crée de nouveaux threads à l'intérieur, n'est-ce pas?
supposons que le lambda crée un Future à exécuter par le forkjoinpool ou un autre exécuteur.