Мне нужен ваш совет, проверка кода или улучшение моей реализации многотонного шаблона. Я хочу поддержку нескольких подключений для сервера mongodb.
public class MongoDatabaseFactory {
private static volatile Map<String, MongoDatabase> connections = new ConcurrentHashMap<String, MongoDatabase>();
public static MongoDatabase getDatabase(Databases database) throws MongoException {
if (null == database) throw new MongoException("Database not found");
if (null == database.name() || database.name().isEmpty()) throw new MongoException("Database not found");
if (!connections.containsKey(database.name()) || null == connections.get(database.name())) {
synchronized (database) {
if (!connections.containsKey(database.name()) || null == connections.get(database.name())) {
connectDB(database);
}
}
}
if (!connections.get(database.name()).isAuthenticated()) {
synchronized (database) {
if (!connections.get(database.name()).isAuthenticated()) {
connectDB(database);
}
}
}
return connections.get(database.name());
}
}
Что лучше всего подходит для многотонной схемы?
2 ответа
Как говорит Марко Топольник , ваше текущее решение не является потокобезопасным.
Я воспринял это как небольшое упражнение и написал следующий универсальный многопоточный шаблон Multition. Разработан ли он для хорошей работы со многими потоками и подходит в случаях, когда создание объекта-значения обходится дорого. Обратите внимание, что я не уверен, что в вашем конкретном случае нет более простого решения.
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadSafeMultition <K, V> {
private final ConcurrentHashMap<K, FutureTask<V>> map = new ConcurrentHashMap<K, FutureTask<V>>();
private ValueFactory<K, V> factory;
public ThreadSafeMultition(ValueFactory<K, V> factory) {
this.factory = factory;
}
public V get(K key) throws InterruptedException, ExecutionException {
FutureTask<V> f = map.get(key);
if (f == null) {
f = new FutureTask<V>(new FactoryCall(key));
FutureTask<V> existing = map.putIfAbsent(key, f);
if (existing != null)
f = existing;
else // Item added successfully. Now that exclusiveness is guaranteed, start value creation.
f.run();
}
return f.get();
}
public static interface ValueFactory<K, V> {
public V create(K key) throws Exception;
}
private class FactoryCall implements Callable<V> {
private K key;
public FactoryCall(K key) {
this.key = key;
}
@Override
public V call() throws Exception {
return factory.create(key);
}
}
}
Эта строка не является поточно-ориентированной:
if (!connections.containsKey(database.name()) || null == connections.get(database.name()))
Здесь у вас будет гонка данных на хеш-карте, потому что вы не защищаете доступ к карте блокировкой. Вероятно, лучшим решением было бы переместить это в блок synchronized
. Здесь не стоит беспокоиться о производительности, по крайней мере, без веских доказательств.
Похожие вопросы
Связанные вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.