Java: как синхронизировать доступ к массиву и каковы ограничения того, что происходит в синхронизированном состоянии - PullRequest
10 голосов
/ 08 сентября 2011

У меня был массив 2x2, на котором работали два потока.

можно ли использовать синхронизированный оператор в Java на массиве?

как работает блокировка? В учебном потоке по Java сказано, что оператор synchronized работает с объектами, поэтому я не был уверен, что они имели в виду. Другой сайт сказал, что я мог бы сделать заявление, как

synchronized (array1[]){

}

Синхронизирует ли это доступ ко всему в массиве, чтобы массив был заблокирован для других потоков?

если у меня есть двумерный массив, могу ли я использовать

synchronized (array1[i]) для блокировки одной из строк массива?

и возможно ли заблокировать отдельные значения массива с чем-то вроде

synchronized (array1[i][j]){

}

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

Ответы [ 4 ]

12 голосов
/ 08 сентября 2011

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

Вы можете синхронизировать блок кода на определенном мониторе, например:

public void myMethod() {

    unsynchronized_statements...

    synchronized(myMonitorObject) {
        synchronized_statments...
    }

Рекомендуется синхронизировать как можно меньше строк кода.

Синхронизация кода на мониторе никак не влияет на монитор, а влияет только на потоки, обращающиеся к синхронизированному блоку.кода.Прежде чем выполнение потока сможет войти в блок кода, он должен получить «блокировку» на мониторе.Среда выполнения Java гарантирует, что не более одного потока одновременно может иметь «блокировку» на мониторе. Таким образом, синхронизация в вашем массиве не запрещает несинхронизированные блоки кода для доступа к нему! Хитрость заключается в том, чтобы убедиться, что все операции, которые вы не хотите выполнять одновременно, находятся внутри блоков, синхронизированных на одном и том жеmonitor.

Поскольку Java не предлагает многомерные массивы, а только массивы массивов, вы, безусловно, можете синхронизировать вложенный массив для более детальной синхронизации.Если вы моделируете 2d-массив как массив строк, вы можете синхронизировать только по строкам, а не по столбцам, поскольку в этом примере столбцы не представлены в виде отдельных массивов.

Вы можете синхронизировать только значения одного массива, если они не являются простыми, поэтому Integer () вместо int.Обратите внимание, что Integer () является неизменным объектом, поэтому вы не сможете изменить его значение.Решением было бы создать свой собственный объект-обертку Cell () с помощью метода get и set для содержащегося числового значения.Это позволит вам позволить потоку получить блокировку ячейки и безопасно изменить ее значение.

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

Классы:

  • Матрица : представление двумерной матрицы ячеек
  • Ячейка : оболочка для значения ячейки матрицы
  • Операция : Абстрактная операция над массивом Ячеек
  • IncrementOperation : Операциякоторый увеличивает значение каждой ячейки
  • ReverseOperation : операция, которая изменяет порядок ячеек
  • Main : приложение

Приложение запускает несколько операций над одной матрицей.Единственный синхронизированный блок кода находится в классе Operation.Если вы удалите синхронизацию, результаты будут неправильными, потому что две операции одновременно манипулируют одной и той же строкой.

Вывод при синхронизации:

[105, 104, 103, 102, 101]
[110, 109, 108, 107, 106]
[115, 114, 113, 112, 111]
[120, 119, 118, 117, 116]
[125, 124, 123, 122, 121]
[130, 129, 128, 127, 126]
[135, 134, 133, 132, 131]
[140, 139, 138, 137, 136]
[145, 144, 143, 142, 141]
[150, 149, 148, 147, 146]

Пример вывода при НЕ синхронизированной:

[105, 4, 103, 102, 101]
[110, 9, 108, 207, 106]
[115, 14, 113, 212, 111]
[120, 19, 118, 217, 116]
[125, 124, 123, 122, 121]
[130, 129, 128, 127, 126]
[135, 34, 133, 232, 131]
[140, 139, 138, 137, 136]
[145, 144, 143, 142, 141]
[150, 149, 148, 147, 146]

Обратите внимание, что я добавил несколько операторов Thread.sleep () в реализации операций, чтобы сделать разницу между синхронизированным и несинхронизированным выполнением более очевидной.

3 голосов
/ 08 сентября 2011

Ключевое слово synchronized работает только с объектами.

Синхронизация с массивом блокирует только массив (в Java массив является объектом).

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

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

3 голосов
/ 08 сентября 2011

Нет, синхронизация таким способом означает только то, что вы блокируете объект массива, но не синхронизируете доступ к его методам. Если ваш массив виден другим, они могут по-прежнему получать доступ / изменять его содержимое независимо от того, заблокирован он или нет. Только они не могут заблокировать его одновременно - если они попытаются (из другого потока), они будут заблокированы до тех пор, пока блокировка не будет снята исходным получателем.

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

class ArrayWrapper {
  private int[] array = ...;

  public void setValue(int index, int value) {
    synchronized (array) {
      array[index] = value;
    }
  }

  public int getValue(int index) {
    synchronized (array) {
      return array[index];
    }
  }

  ...
}
1 голос
/ 08 сентября 2011

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

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