La méthode remove()
n'est pas entièrement claire. Dans sa forme actuelle, c'est une boucle infinie, d'abord, il va itérer sur les éléments de la tête et les supprimer, jusqu'à ce que la condition n'est pas remplie pour l'élément de tête, puis il interrogera à plusieurs reprises pour cet élément de tête et réévaluer la condition . À moins, il réussit à enlever tous les éléments, auquel cas il renflouera avec une exception.
Si vous voulez traiter tous les éléments présents dans la carte, vous pouvez simplement faire une boucle dessus, les itérateurs faiblement cohérents vous permettent de continuer en le modifiant; vous pouvez remarquer des mises à jour simultanées en cours ou non. Si vous souhaitez traiter les éléments de tête correspondants uniquement, vous devez insérer une condition, retourner à l'appelant ou mettre le thread en veille (ou mieux ajouter un mécanisme de notification), pour éviter de graver le processeur avec un test échoué répété (ou même jeter quand la carte est vide). En outre, vous pouvez implémenter les opérations en utilisant ConcurrentSkipListMap
lorsque vous vous assurez qu'il n'y a pas d'interférence entre les fonctions. En supposant remove
est censé traiter tous les éléments actuels une fois, la mise en œuvre peut ressembler à
public void add(LocalDateTime time, Task task) {
tasks.merge(time, Collections.singletonList(task),
(l1,l2) -> Stream.concat(l1.stream(),l2.stream()).collect(Collectors.toList()));
}
public void remove() {
for(Map.Entry<LocalDateTime, List<Task>> keyVal : tasks.entrySet()) {
final List<Task> values = keyVal.getValue();
if(isSomeCondition(keyVal) && tasks.remove(keyVal.getKey(), values)) {
for (Task t : values) {
//do task processing
}
}
}
}
Le point clé est que les listes contenues dans la carte ne sont jamais modifiés. L'opération merge(time, Collections.singletonList(task), …
va même stocker une liste immuable d'une seule tâche s'il n'y avait pas de mappage précédent. Dans le cas où il existe des tâches précédentes, la fonction de fusion (l1,l2) -> Stream.concat(l1.stream(),l2.stream()).collect(Collectors.toList())
créera une nouvelle liste plutôt que de modifier celles qui existent déjà. Cela peut avoir un impact sur les performances lorsque les listes deviennent beaucoup plus grandes, en particulier lorsque l'opération doit être répétée en cas de conflit, mais c'est le prix à payer pour ne pas avoir besoin de verrouillage ou de synchronisation supplémentaire.
L'opération remove
utilise la méthode remove(key, value)
qui ne réussit que si la valeur de la carte correspond toujours à celle attendue.Cela repose sur le fait qu'aucune de nos méthodes ne modifie jamais les listes contenues dans la carte, mais les remplace par de nouvelles instances de liste lors de la fusion. Si remove(key, value)
réussit, la liste peut être traitée; à ce moment, il n'est plus contenu dans la carte. Notez que pendant l'évaluation de isSomeCondition(keyVal)
, la liste est toujours contenue dans la carte, par conséquent, isSomeCondition(keyVal)
ne doit pas le modifier, cependant, je suppose que cela devrait être le cas pour une méthode de test comme isSomeCondition
de toute façon. Bien sûr, l'évaluation de la liste au sein de isSomeCondition
repose également sur les autres méthodes ne modifiant jamais la liste.
Avez-vous besoin de 'ConcurrentSkipListMap' spécifiquement? 'ConcurrentHashMap' a des garanties d'atomicité pour des opérations comme' compute' et 'computeIfAbsent' qui pourraient être très utiles ici. – shmosel
@shmosel Je pense que oui parce que je veux stocker mes disques commandés par LocalDateTime – Sergey
@shmosel ce n'est pas clair pour moi quelles garanties de CHM vous aident réellement ici comparé à CSLM. –