Подтвердить что ты не робот

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

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

EDIT: В частности, массив является либо примитивным массивом, либо неизменным массивом объектов. Ex. Массив int или массив String.

4b9b3361

Ответ 1

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

Определяется в JLS §17.6 Word Tearing:

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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

Ответ 7

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

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

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

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

Ответ 8

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

https://www.youtube.com/watch?v=VCattsfHR4o

Ответ 9

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

При использовании индекса - некоторые счетчики → 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)));
}
}

Ответ 10

В общем, да. Вместо этого попробуйте Vector.