J'ai découvert une situation lors du débogage du code, c'est-à-dire que lorsque j'ai vérifié les connexions de memcached, j'ai constaté qu'elles étaient toujours maintenues à environ 100. Bien sûr, cela ne semble pas poser de problème, car memcached a 1024 connexions par défaut. Mais ce à quoi je pense, c'est pourquoi il y en a une centaine. Parce que mon memcachedclient est généré en mode singleton, j'ai défini une classe memcachedClientFactory. Le code principal est le suivant :
privé MemcachedClientFactory(){
}
private MemcachedClientFactory (MemcachedConnectionBuilder memcachedConnectionBuilder, serveurs String){
this.memcachedConnectionBuilder= memcachedConnectionBuilder ;
this.servers=serveurs ;
}
public statique MemcachedClient createClient(){
si(memcachedClient==null){
this.memcahcedClien= new MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
}
renvoie this.memcahcedClient ;
}
}
}
Revenons à la question initiale, pourquoi y a-t-il plus de 100 connexions ?
La méthode d'écriture ci-dessus peut-elle vraiment garantir qu'une seule connexion sera générée ? Evidemment non, pourquoi ? Concurrence multithread ! Le problème réside ici. Lorsque plusieurs threads entrent dans la méthode createClient() en même temps et qu'ils décident tous que memcachedClient est nul, plusieurs connexions sont générées. Ha, le problème a été trouvé.
améliorer:
Ce n'est pas grave, le changement est très simple. Il n'y a aucun problème avec le programme et il est garanti qu'il n'y a qu'une seule connexion.
Mais en mettant ce problème de côté, nous pouvons continuer à réfléchir profondément à la manière de résoudre le problème de concurrence en mode singleton.
Permettez-moi de résumer, il existe environ trois façons de résoudre le problème de concurrence en mode singleton :
1. N'utilisez pas l'instanciation retardée, mais utilisez l'instanciation anticipée.
Autrement dit, le programme est réécrit comme suit :
public statique Singleton getInstance(){
instance de retour ;
}
}
En faisant cela, la JVM crée l'instance immédiatement lors du chargement de la classe, donc le principe est que la charge de création de l'instance n'est pas importante, je ne pense pas trop aux performances, et nous confirmons que l'instance sera certainement être utilisé. En fait, mon code précédent peut également être utilisé de cette façon :
privé MemcachedClientFactory(){
}
private MemcachedClientFactory (MemcachedConnectionBuilder memcachedConnectionBuilder, serveurs String){
this.memcachedConnectionBuilder= memcachedConnectionBuilder ;
this.servers=serveurs ;
}
public statique MemcachedClient createClient(){
renvoie this.memcahcedClient ;
}
}
}
Cependant, il semble qu'il n'y ait pas de problème, mais il y a un danger caché, c'est-à-dire qu'une fois que quelqu'un appelle accidentellement la méthode memcachedClient.shutdown(), l'ensemble du programme ne pourra pas générer un nouveau memcachedClient. Bien sûr, il s’agit d’un cas extrême, mais pour des raisons de robustesse du code, cela peut être modifié comme suit :
2. Utilisez simplement le mot-clé synchronisé.
Cela peut entraîner des problèmes de synchronisation, mais nous savons que la surcharge liée à l'utilisation de synchronisé est très élevée et affectera sérieusement les performances. Le principe de son utilisation est donc que vous confirmiez que vous n'appellerez pas cette méthode fréquemment, ou que la surcharge liée à la création cette instance ne sera pas spéciale. Si cela peut être amélioré, voir ci-dessous.
3. Utilisez le "verrouillage à double vérification" et utilisez la synchronisation dans getInstance
privé Singleton(){};
public statique Singleton getInstance(){
si(instance==null){
synchronisé (Singleton.class){
si(instance==null){
instance=nouveau Singleton();
}
}
}
instance de retour ;
}
}