Точные данные на карте доступны многим потокам - PullRequest
0 голосов
/ 01 сентября 2018

Я пытаюсь отсортировать объекты по пяти отдельным группам в зависимости от веса, данного им при создании экземпляра.

Теперь я хочу отсортировать эти объекты по пяти группам по весам. Для этого каждый из них должен сравниваться с другим.

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

Группы были настроены как две разные карты. Первым из них является Hashtable, который приводит к сбою программы, которая выдает неизвестный ConcurrencyIssue. Когда я использую ConcurrentHashMap, данные неверны, потому что они не удаляют запись во времени до того, как следующий объект сравнивается с ConcurrentHashmap. Так что это вызывает логическую ошибку и приводит к группам, которые отсортированы правильно только половину времени.

Мне нужно, чтобы хэш-карта немедленно удаляла запись из карты до того, как произойдет следующий сортировка ... Я думал, что синхронизация функции сделает это, но она все равно не работает.

Есть ли лучший способ сортировки объектов друг против друга, которые добавляются в структуру данных рабочими потоками? Спасибо! Я немного запутался в этом.

private synchronized void sortingHat(Moment moment) {
    try {
        ConcurrentHashMap[] helperList = {postedOverlays, chanl_2, chanl_3, chanl_4, chanl_5};

        Moment moment1 = moment;

        //Iterate over all channels going from highest channel to lowest
        for (int i = channelCount - 1; i > 0; i--) {

            ConcurrentHashMap<String, Moment> table = helperList[i];

            Set<String> keys = table.keySet();

            boolean mOverlap = false;

            double width = getWidthbyChannel(i);

            //If there is no objects in table, don't bother trying to compare...
            if (!table.isEmpty()) {

                //Iterate over all objects currently in the hashmap
                for (String objId : keys) {
                    Moment moment2 = table.get(objId);

                    //x-Overlap
                    if ((moment2.x + width >= moment1.x - width) ||
                            (moment2.x - width <= moment1.x + width)) {

                        //y-Overlap                           
                        if ((moment2.y + width >= moment1.y - width) ||
                                (moment2.y - width <= moment1.y + width)) {

                            //If there is overlap, only replace the moment with the greater weight.
                            if (moment1.weight >= moment2.weight) {
                                mOverlap = true;
                                table.remove(objId);
                                table.put(moment1.id, moment1);
                            }
                        }
                    }
                }
            }

            //If there is no overlap, add to channel anyway
            if (!mOverlap) {
                table.put(moment1.id, moment1);
            }

        }
    } catch (Exception e) {
        Log.d("SortingHat", e.toString());
    }
}

В table.remove(objId) возникают проблемы. Момент А отправляется в функцию сортировки и не имеет проблем. Добавляется момент B, он перекрывается, он сравнивается с моментом A. Если момент B меньше веса, чем момент A, все в порядке. Если момент B весит больше, и A необходимо удалить, то когда момент C будет отсортирован, момент A все еще будет в хэш-карте вместе с моментом B. И поэтому, похоже, именно в этом и заключается логическая ошибка.

1 Ответ

0 голосов
/ 02 сентября 2018

У вас проблема с синхронизацией.

Синхронизация, которую вы используете, будет синхронизироваться с помощью блокировки «this». Вы можете представить это так:

public synchronized void foo() { ... }

совпадает с

public void foo() {
    synchronized(this) {
        ....
    }
}

Это означает, что перед входом текущий Поток попытается получить «этот объект» в качестве блокировки. Теперь, если у вас есть рабочий поток, который также имеет синхронизированный метод (для добавления материала в таблицу), они не будут полностью исключать друг друга. Вы хотели, чтобы один поток завершил свою работу, прежде чем следующий сможет начать свою работу.

Первым из них является Hashtable, который завершает работу программы, выбрасывая неизвестный ConcurrencyIssue.

Это проблема, потому что может случиться так, что 2 потока вызывают что-то одновременно. Чтобы проиллюстрировать это, представьте себе, что один поток вызывает метод put (ключ, значение), а другой поток вызывает вызов (ключ). Если эти вызовы выполняются в одно и то же время (например, различными ядрами), каким будет в результате HashTable? Поскольку никто не может сказать наверняка, будет выдано ConcurrentModificationException . Примечание: это очень простое объяснение!

Когда я использую ConcurrentHashMap, данные неверны, потому что они не удаляют запись во времени перед сравнением следующего объекта с ConcurrentHashmap

ConcurrentHashMap - это утилита, позволяющая избежать проблем с параллелизмом, это не волшебная, многофункциональная охота на единорога, нож для масла. Он синхронизирует вызовы метода, что приводит к тому, что только один поток может добавлять, удалять или выполнять какие-либо другие операции с HashMap. Он не обладает той же функциональностью, что и блокировка некоторого вида, что приведет к тому, что доступ к карте будет выделен в потоке.

Может быть один поток, который хочет вызвать add, и один, который хочет вызвать remove. ConcurrentHashMap ограничивает только те вызовы, которые не могут происходить одновременно. Что приходит первым? У вас есть власть над этим (в этом сценарии). Что вы хотите, так это чтобы один поток закончил свою работу, прежде чем следующий сможет выполнить свою работу.

То, что вам действительно нужно, зависит от вас. Пакет java.util.concurrent содержит целый арсенал классов, которые вы можете использовать. Например:

Вы можете использовать lock для каждой Карты. При этом каждый поток (либо сортировка / удаление / добавление, либо что-либо еще) может сначала получить блокировку для указанной карты и затем работать с этой картой, например так:

public Worker implements Runnable {
    private int idOfMap = ...;
    @Override
    public void run() {
        Lock lock = getLock(idOfMap);
        try {
            lock.lock();
            // The work goes here
            //...
        } finally {
            lock.unlock();
        }
    }
}

Строка lock.lock () гарантирует, что нет другого Thread, который в настоящее время работает над картой и модифицирует ее, после того, как вызов метода вернется, и поэтому этот поток получит взаимный доступ к карте. Никакой сортировки, пока вы не закончите удаление нужного элемента.

Конечно, вам как-то придется удерживать указанные блокировки, как в объекте данных. С учетом вышесказанного, вы также можете использовать Семафор, синхронизированный (map) в каждом потоке или сформулировать свою работу на карте в форме Runnables и передать их другому потоку, который вызывает все Runnables, которые он получил, один за другим. Возможности почти безграничны. Я лично рекомендовал бы начинать с блокировки.

...