потокобезопасность массива Java - PullRequest
23 голосов
/ 15 июля 2009

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

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

 class Test1
 {
     static final private int N = 4096;
     final private int[] x = new int[N];
     final private AtomicInteger nwritten = new AtomicInteger(0);
     // invariant: 
     // all values x[i] where 0 <= i < nwritten.get() are immutable

     // read() is not synchronized since we want it to be fast
     int read(int index) {
         if (index >= nwritten.get())
             throw new IllegalArgumentException();
         return x[index];
     }
     // write() is synchronized to handle multiple writers
     // (using compare-and-set techniques to avoid blocking algorithms
     // is nontrivial)
     synchronized void write(int x_i) {
         int index = nwriting.get();
         if (index >= N)
             throw SomeExceptionThatIndicatesArrayIsFull();
         x[index] = x_i;
         // from this point forward, x[index] is fixed in stone
         nwriting.set(index+1);
     }     
 }

edit: Критика этого примера - не мой вопрос, я буквально просто хочу знать, создает ли доступ к одному индексу массива одновременно с другим индексом проблемы параллелизма, не мог придумать простого пример.

Ответы [ 5 ]

14 голосов
/ 15 июля 2009

Хотя вы не получите недопустимое состояние при изменении массивов, как вы упоминали, у вас будет та же проблема, которая возникает, когда два потока просматривают энергонезависимое целое число без синхронизации (см. Раздел Обучающее руководство по Java на Память Ошибки согласованности ). По сути, проблема в том, что поток 1 может записать значение в пространство i, но нет гарантии, когда (или если) поток 2 увидит изменение.

Класс java.util.concurrent.atomic.AtomicIntegerArray делает то, что вы хотите сделать.

4 голосов
/ 15 июля 2009

В примере много вещей, которые отличаются от прозаического вопроса.

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

Однако модель памяти Java не гарантирует (насколько мне известно), что значение, записанное одним потоком, будет видимым для другого потока, если вы не синхронизируете доступ.

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

1 голос
/ 15 июля 2009

Поскольку read () не синхронизирован, у вас может быть следующий сценарий:

Thread A enters write() method
Thread A writes to nwriting = 0;
Thread B reads from nwriting =0;
Thread A increments nwriting. nwriting=1
Thread A exits write();

Поскольку вы хотите гарантировать, что ваши переменные адреса никогда не конфликтуют, как насчет чего-то вроде (дисконтирование проблем индекса массива):

int i;
synchronized int curr(){ return i; }
synchronized int next(){ return ++i;}

int read( ) {
         return values[curr()];
     }

void write(int x){
   values[next()]=x;
}
1 голос
/ 15 июля 2009

Да, поскольку неправильное чередование кэша все еще может происходить в среде с несколькими процессорами и ядрами. Есть несколько вариантов, чтобы избежать этого:

  • Использование небезопасной частной библиотеки Sun для атомарной установки элемента в массиве (или добавленная функция jsr166y в Java7
  • Использовать массив AtomicXYZ []
  • Использовать пользовательский объект с одним изменяемым полем и иметь массив этого объекта.
  • Вместо этого используйте в своем алгоритме приложение ParallelArray из jsr166y
1 голос
/ 15 июля 2009

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

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