Требуется ли в Java синхронизировать доступ на запись к массиву, если каждый поток выполняет запись в отдельное пространство ячеек? - PullRequest
26 голосов
/ 19 мая 2011

Требуется ли синхронизировать доступ на запись к массиву в Java, если каждый поток выполняет запись в отдельное пространство ячейки?

РЕДАКТИРОВАНИЕ: В частности, массив является либо примитивным массивом, либо массивом неизменяемых объектов. Ex. Массив int или массив String.

Ответы [ 10 ]

20 голосов
/ 19 мая 2011

Нет, синхронизация не требуется.

Она определена в JLS §17.6. Разрыв слова :

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

4 голосов
/ 19 мая 2011

Если доступ для чтения также разделен таким же образом, то синхронизация не требуется по ссылке bkail.

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

2 голосов
/ 19 мая 2011

Не простая да или нет проблема.Некоторые вещи для рассмотрения:

  • Ваш вопрос подразумевает, что вы храните примитивные типы (или ссылки) в вашем массиве.Выполнение сложных операций над объектами, хранящимися в массиве, может потребовать синхронизации.
  • Изменение значений long или double небезопасно без синхронизации
  • Даже если вы хранитеПримитивы, которые не double или long, могут привести к тому, что другие потоки не смогут сразу увидеть измененные значения из-за кэширования (в результате устаревшие чтения )
1 голос
/ 19 мая 2011

Те же темы читают и значение. Если так, то ты в порядке. Если нет, то вам нужно беспокоиться о том, видят ли другие потоки самые последние значения. Об этом обычно заботятся через ключевую работу volatile или через атомарные переменные.

For example if you have an int [] intArray with three elements.

thread 1 updates intArray[0]=<new value>
thread 2 updates intArray[1]=<new value>
thread 3 updates intArray[2]=<new value>

если эти потоки также используются для чтения значений, то вы в порядке, но если новый поток -> поток 4 пытается прочитать значения, то нет никакой гарантии, что он увидит последнее назначение.

все будет в порядке, если вы используете AtomicIntegerArray, AtomicLongArray или AtomicReferenceArray

1 голос
/ 19 мая 2011

Вы можете делать то, что просите, обновляя содержимое каждого индекса, , но вам не гарантируется , что другие потоки, считывающие данные в индексах, видят текущие данные.

Естьключевое слово в Java, называемое volatile, которое отмечает переменные экземпляра и класса, чтобы JVM знала, что эти значения должны измениться, и не будет выполнять какую-либо оптимизацию кэша чтения , поскольку потоки чтения могут получить устаревшие данные .Поскольку вы не можете пометить содержимое индекса массива volatile, другие читатели могут получить устаревшие данные.

Использование raw Arrays в Java является хорошей практикой только в очень специфических сценариях.Ваш может быть одним из таких случаев, но я не могу сказать из вопроса, поэтому я предлагаю вам взглянуть на java.util.concurrent , в частности CopyOnWriteArrayList и CopyOnWriteArraySet если вам не нужны дубликаты.

По какой-то причине они являются частью стандартной библиотеки, и если вы занимаетесь многопоточностью и совместным использованием данных, вы должны быть как можно ближе знакомы с java.util.concurrent, насколько это возможно..

1 голос
/ 19 мая 2011

Обычно вам нужно синхронизироваться, если вы хотите, чтобы другие потоки всегда могли видеть последнее записанное вами значение. Но я мог бы в некоторых случаях, скажем, предварительно вычислить гигантско дорогой long [] (где каждая запись заняла бы много ЦП), и вы бы делали это из нескольких потоков, зная, что нет два из этих потоков должны писать в одну и ту же ячейку. Затем, когда все ваши потоки будут завершены, вы будете использовать синхронизацию один раз, чтобы убедиться, что каждое последующее чтение будет видеть правильные значения. Нечто подобное.

Обратите внимание, что если вы не хотите заниматься проблемами синхронизации, вы можете посмотреть на классы, такие как AtomicLongArray . Как дополнительное преимущество, такие классы, как AtomicLongArray могут быть поддержаны реализацией, которую невозможно воссоздать на 100% Java.

0 голосов
/ 19 августа 2015

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

При использовании индекса-- некоторые счетчики> 1 (все должны быть 1, в противном случае взаимное исключение нарушается)

public class App {


static int threads = 1000;
static int maxIndex = 1000;

public static void main(String[] args)
{
    try {
        testThreads();
    } catch (InterruptedException ex) {
        Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
    }
    return;

}

public static void testThreads() throws InterruptedException {

    AtomicInteger[] ids = new AtomicInteger[maxIndex];
    for (int i = 0; i < maxIndex; i++) {
        ids[i] = new AtomicInteger(0);  
    }
    Executor exec = Executors.newFixedThreadPool(threads);
    AtomicInteger index = new AtomicInteger(maxIndex);
    final CountDownLatch startGate = new CountDownLatch(1);
    final CountDownLatch endGate = new CountDownLatch(threads);

    for (int i = 0; i < threads; i++) {
        exec.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    startGate.await();
                    try {
                        int i = maxIndex;
                        while (i > 0) {
                            /** 
                             * Interchanging this lines force or avoid collisions. 
                             */
                         // i = --maxIndex;
                            i = index.decrementAndGet();
                            ids[i].incrementAndGet(); 
                        }
                    } catch(Exception ignored) {
                        System.out.println(ignored);
                    } finally {
                        endGate.countDown();
                    }
                } catch (InterruptedException ignored) {
                }
            }
        });
    }
    startGate.countDown();
    endGate.await();
    System.out.println(new ArrayList(Arrays.asList(ids)));
}
}
0 голосов
/ 08 июля 2015

Не пишите напрямую на общий ресурс. Вы заставите многопоточную программу работать хуже, чем ее счетчик. Эффект уничтожения в Cache, поскольку Cache выбирается и аннулируется в блоках. Посмотрите на этот разговор, если вы хотите узнать больше. Используйте volatile и синхронизируйте его лучше, чем ничего, но все равно медленно.

https://www.youtube.com/watch?v=VCattsfHR4o
0 голосов
/ 11 августа 2013

Аналогичный вопрос, заданный для списка рассылки Concurrency Interest - « Атомарные операции с байтами []? ».

Дэвид Холмс сказал:

Чтение / записьразличные области массива должны действовать как доступ к независимым массивам.Это то, что гарантирует отсутствие слов.Независимо от того, как массив реализован в виртуальной машине, виртуальная машина должна убедиться, что это справедливо для базовых обращений к массиву.

Как я уже сказал, не совсем понятно, будет ли задействована собственная версия arraycopy.

0 голосов
/ 19 мая 2011

В общем да. Попробуйте вместо Vector.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...