Я обнаружил ситуацию во время отладки кода, то есть, когда я проверял соединения memcached, я обнаружил, что оно всегда поддерживалось на уровне около 100. Конечно, это вроде бы не проблема, потому что у memcached по умолчанию 1024 соединения. Но я думаю о том, почему их около 100. Поскольку мой memcachedclient генерируется в одноэлементном режиме, я определил класс memcachedClientFactory. Основной код выглядит следующим образом:
частный MemcachedClientFactory(){
}
частный MemcachedClientFactory (MemcachedConnectionBuilder memcachedConnectionBuilder, строковые серверы) {
this.memcachedConnectionBuilder= memcachedConnectionBuilder;
this.servers=серверы;
}
общественный статический MemcachedClient createClient(){
если (memcachedClient == null) {
this.memcahcedClien= новый MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
}
вернуть this.memcahcedClient;
}
}
}
Возвращаясь к исходному вопросу: почему соединений больше 100?
Может ли описанный выше метод записи действительно гарантировать, что будет создано только одно соединение? Очевидно, нет, почему? Многопоточный параллелизм! Проблема заключается здесь. Когда несколько потоков одновременно входят в метод createClient() и все они решают, что memcachedClient имеет значение null, создается несколько соединений. Ха, проблема нашлась.
улучшать:
Это нормально, изменение очень простое. С программой проблем нет, и гарантированно есть только одно соединение.
Но оставив эту проблему в стороне, мы можем продолжить глубоко думать о том, как решить проблему параллелизма в одноэлементном режиме.
Подведу итог: существует примерно три способа решения проблемы параллелизма в одноэлементном режиме:
1. Не используйте отложенное создание экземпляра, а используйте раннее создание экземпляра.
То есть программа переписывается как:
публичный статический синглтон getInstance(){
вернуть экземпляр;
}
}
При этом jvm создает экземпляр сразу при загрузке класса, поэтому предпосылка этого заключается в том, что нагрузка на создание экземпляра невелика, я не слишком много думаю о производительности, и мы подтверждаем, что экземпляр обязательно будет быть использован. Фактически, мой предыдущий код также можно использовать таким образом:
частный MemcachedClientFactory(){
}
частный MemcachedClientFactory (MemcachedConnectionBuilder memcachedConnectionBuilder, строковые серверы) {
this.memcachedConnectionBuilder= memcachedConnectionBuilder;
this.servers=серверы;
}
общественный статический MemcachedClient createClient(){
вернуть this.memcahcedClient;
}
}
}
Однако вроде бы проблемы нет, а есть скрытая опасность: если кто-то случайно вызовет метод memcachedClient.shutdown(), вся программа не сможет сгенерировать новый memcachedClient. Конечно это крайний случай, но для надежности кода его можно изменить на:
2. Просто используйте ключевое слово синхронизировано.
Это может привести к проблемам с синхронизацией, но мы знаем, что накладные расходы на использование Synchronized очень высоки и серьезно повлияют на производительность, поэтому предпосылка использования этого метода заключается в том, что вы подтверждаете, что не будете часто вызывать этот метод или что накладные расходы на создание этот экземпляр не будет особенным. Можно ли его улучшить, смотрите ниже.
3. Используйте «двойную проверку блокировки» и используйте синхронизацию в getInstance.
частный синглтон () {};
публичный статический синглтон getInstance(){
если (экземпляр == ноль) {
синхронизировано (Singleton.class){
если (экземпляр == ноль) {
экземпляр = новый синглтон ();
}
}
}
вернуть экземпляр;
}
}