ConcurrentHashMap.get (), гарантировал, что видел предыдущий ConcurrentHashMap.put () другим потоком?

Работа с varchars является существенно медленной и неэффективной по сравнению с работой с численными данными по очевидным причинам. Функции, с которыми Вы связываетесь в исходном сообщении, действительно будут довольно медленными, поскольку они циклично выполняются через каждый символ в строке, чтобы определить, является ли это числом. Сделайте это для тысяч записей и процесса обязано быть медленным. Это - идеальное задание для Регулярных выражений, но они исходно не поддерживаются в SQL Server. Можно добавить поддержку с помощью функции CLR, но трудно сказать, насколько медленный это будет, не пробуя его, я определенно ожидал бы, что он будет значительно быстрее, чем цикличное выполнение через каждый символ каждого номера телефона, однако!

, Как только Вы отформатировали номера телефона в Вашей базе данных так, чтобы они были только числами, Вы могли переключиться на числовой тип в SQL, который приведет к молниеносным сравнениям с другими числовыми типами. Вы могли бы найти, что, в зависимости от то, как быстро Ваши новые данные входят, делая обрезку и преобразование в числовой на стороне базы данных, является много достаточно быстро однажды, по сравнению с чем Вы, правильно отформатирован, но, если бы возможно, Вы были бы более обеспеченной записью утилиты импорта на языке.NET, который заботился бы об этих проблемах форматирования прежде, чем поразить базу данных.

Так или иначе, хотя, Вы собираетесь иметь большую проблему относительно дополнительного форматирования. Даже если Ваши числа, как будут гарантировать, будут только североамериканскими в источнике, некоторые люди поместят 1 перед квалифицированным номером телефона полностью кода зоны, и другие не будут, который вызовет потенциал для многократных въездов того же номера телефона. Кроме того, в зависимости от того, что представляют Ваши данные, некоторые люди будут использовать свой домашний телефон, который мог бы иметь несколько человек, живущих там, таким образом, ограничение на уникальность данных на него только позволит одному участнику базы данных на домашнее хозяйство. Некоторые использовали бы их число работы и имели бы ту же проблему, и некоторые будут или не включать расширение, которое вызовет искусственный потенциал уникальности снова.

Все это может или не может повлиять на Вас, в зависимости от Ваших конкретных данных и использований, но важно иметь в виду!

22
задан Stu Thompson 20 November 2009 в 14:36
поделиться

5 ответов

Наблюдаем ли мы интересное проявление модели памяти Java? При каких условиях регистры сбрасываются в основную память? Я думаю, что гарантировано, что если два потока синхронизируются на одном и том же объекте, тогда они увидят согласованное представление памяти.

Я не знаю, что Semphore делает внутренне, он почти очевидно должен выполнять некоторую синхронизацию, но знаем ли мы это?

Что произойдет, если вы сделаете

synchronize(dedicatedLockObject)

вместо получения семафора?

0
ответ дан 29 November 2019 в 05:29
поделиться

Я не думаю, что проблема в "ConcurrentHashMap", а скорее где-то в вашем коде или в рассуждениях о вашем коде. Я не могу обнаружить ошибку в приведенном выше коде (может быть, мы просто не видим плохой части?).

Но отвечу на ваш вопрос: «Гарантированно ли ConcurrentHashMap.get () увидит предыдущий ConcurrentHashMap.put () другой веткой? " Я собрал небольшую тестовую программу.

Короче: Нет, ConcurrentHashMap в порядке!

Если карта написана плохо, следующая программа должна вывести "Плохой доступ!" по крайней мере, время от времени. Он генерирует 100 потоков со 100000 обращениями к методу, который вы описали выше. Но печатает «Все ок!».

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class Test {
    private final static ConcurrentHashMap<String, Test> map = new ConcurrentHashMap<String, Test>();
    private final static Semaphore lock = new Semaphore(1);
    private static int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(100);
        List<Callable<Boolean>> testCalls = new ArrayList<Callable<Boolean>>();
        for (int n = 0; n < 100000; n++)
            testCalls.add(new Callable<Boolean>() {
                @Override
                public Boolean call() throws Exception {
                    doSomething(lock);
                    return true;
                }
            });
        pool.invokeAll(testCalls);
        pool.shutdown();
        pool.awaitTermination(5, TimeUnit.SECONDS);
        System.out.println("All ok!");
    }

    static void doSomething(Semaphore lock) throws InterruptedException {
        boolean haveLock = lock.tryAcquire(3000, TimeUnit.MILLISECONDS);

        if (haveLock) {
            Test foo = map.get("key");
            if (foo == null) {
                foo = new Test();
                map.put("key", new Test());
                if (counter > 0)
                    System.err.println("Bad access!");
                counter++;
            }
            lock.release();
        } else {
            System.err.println("Fail to lock!");
        }
    }
}
2
ответ дан 29 November 2019 в 05:29
поделиться

Обновление: putIfAbsent () здесь логически корректно, но не позволяет избежать проблемы создания Foo только в том случае, если ключ отсутствует. Он всегда создает Foo, даже если не помещает его на карту. Дэвид Руссель ответил хорошо, если предположить, что вы можете принять зависимость Google Collections в своем приложении.


Может быть, мне не хватает чего-то очевидного, но почему вы охраняете карту с помощью семафора? ConcurrentHashMap (CHM) является потокобезопасным (при условии, что он безопасно опубликован, что находится здесь). Если вы пытаетесь получить атомарный вариант «поместите, если еще не там», используйте chm. putIfAbsent () . Если вам нужны более согласованные инварианты, в которых содержимое карты не может измениться, вам, вероятно, потребуется использовать обычный HashMap и синхронизировать его как обычно.

Чтобы ответить на ваш вопрос более прямо: как только ваш пут возвращается, значение, которое вы поместили на карту, гарантированно будет видно следующему потоку, который его ищет.

Боковое примечание, просто +1 к некоторым другим комментариям насчет помещения выпуска семафора в окончательный вариант.

if (sem.tryAcquire(3000, TimeUnit.MILLISECONDS)) {
    try {
        // do stuff while holding permit    
    } finally {
        sem.release();
    }
}
1
ответ дан 29 November 2019 в 05:29
поделиться

Эта проблема создания дорогостоящего для создания объекта в кэше из-за того, что его не удалось найти в кэше, является известной проблемой. И, к счастью, это уже было реализовано.

Вы можете использовать MapMaker из Google Collecitons . Вы просто даете ему обратный вызов, который создает ваш объект, и если клиентский код просматривает карту, а карта пуста, вызывается обратный вызов и результат помещается на карту.

См. javadocs MapMaker ...

 ConcurrentMap<Key, Graph> graphs = new MapMaker()
       .concurrencyLevel(32)
       .softKeys()
       .weakValues()
       .expiration(30, TimeUnit.MINUTES)
       .makeComputingMap(
           new Function<Key, Graph>() {
             public Graph apply(Key key) {
               return createExpensiveGraph(key);
             }
           });

Кстати, в вашем исходном примере нет никаких преимуществ в использовании ConcurrentHashMap, поскольку вы блокируете каждый доступ, почему бы просто не использовать обычную HashMap внутри заблокированного раздела?

10
ответ дан 29 November 2019 в 05:29
поделиться

Следует учесть, что ваши ключи равны и имеют ли одинаковые хэш-коды в оба момента вызова get. Если это просто String , тогда да, здесь проблем не будет. Но поскольку вы не указали общий тип ключей и опускали «неважные» детали в псевдокоде, мне интересно, используете ли вы другой класс в качестве ключа.

В любом случае вы можете захотеть дополнительно зарегистрируйте хэш-код ключей, используемых для получения / ввода в потоках 1 и 2. Если они отличаются, у вас есть проблема. Также обратите внимание, что key1.equals (key2) должно быть истинным; это не то, что вы можете зарегистрировать окончательно, но если ключи не являются окончательными классами, было бы стоить зарегистрировать их полное имя класса, затем посмотрите на метод equals () для этого класса / классов, чтобы увидеть, возможно ли, что второй ключ может считаться неравным первому.

И, чтобы ответить на ваш заголовок - да, ConcurrentHashMap.get () гарантированно увидит любой предыдущий метод put (), где «предыдущий» означает, что между ними существует связь происходит до , как определено моделью памяти Java. (В частности, для ConcurrentHashMap это, по сути, то, что вы ожидаете, с оговоркой, что вы не сможете определить, что произойдет первым, если оба потока выполняются «точно в одно и то же время» на разных ядрах. Однако в вашем случае , вы обязательно должны увидеть результат put () в потоке 2).

И чтобы ответить на ваш заголовок - да, ConcurrentHashMap.get () гарантированно увидит любой предыдущий put (), где «предыдущий» означает, что между ними существует связь происходит до , как указано Модель памяти Java. (В частности, для ConcurrentHashMap это, по сути, то, что вы ожидаете, с оговоркой, что вы не сможете определить, что произойдет первым, если оба потока выполняются «точно в одно и то же время» на разных ядрах. Однако в вашем случае , вы обязательно должны увидеть результат put () в потоке 2).

И чтобы ответить на ваш заголовок - да, ConcurrentHashMap.get () гарантированно увидит любой предыдущий put (), где «предыдущий» означает, что между ними существует связь происходит до , как указано Модель памяти Java. (В частности, для ConcurrentHashMap это, по сути, то, что вы ожидаете, с оговоркой, что вы не сможете определить, что произойдет первым, если оба потока выполняются «точно в одно и то же время» на разных ядрах. Однако в вашем случае , вы обязательно должны увидеть результат put () в потоке 2).

по сути, это то, что вы ожидаете, с оговоркой, что вы не сможете определить, что произойдет первым, если оба потока выполняются «точно в одно и то же время» на разных ядрах. Однако в вашем случае вы обязательно должны увидеть результат put () в потоке 2).

по сути, это то, что вы ожидаете, с оговоркой, что вы не сможете определить, что произойдет первым, если оба потока выполняются «точно в одно и то же время» на разных ядрах. Однако в вашем случае вы обязательно должны увидеть результат put () в потоке 2).

3
ответ дан 29 November 2019 в 05:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: