Синхронизированные блоки Java - PullRequest
4 голосов
/ 04 августа 2010

Если у нас есть метод:

public void doSomething(){
    synchronized(this){
        //some code processing here
    }
    String temp = "init"; //instead of i++
    synchronized(this){
        //some other code processing here
    }
}

Этот метод эквивалентен public synchronized void doSomething()?

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

  • Thread1 входит в первый синхронизированный блок.
  • Thread2 блоков.
  • Thread1 продолжается с i++ и перемещается во второй синхронизированный блок, в то время как Thread2 остается заблокированным.
  • В результате Thread2 входит в метод после того, как Thread1 вышел из обоих синхронизированных блоков.

Все, что мне нужно знать, это:

  • Можно ли рассчитывать на все контексты выполнения, что оба потока ( Thread1 и Thread2 ) могут быть в методе одновременно? Например, Thread2 в первом блоке синхронизации и Thread1 во втором блоке синхронизации для достижения параллелизма.
  • Будут ли некоторые потоки выполнения, где только один поток будет в методе (одновременно) эффективно сериализировать весь поток, делая его эквивалентным public synchronized void doSomething()?

Ответы [ 6 ]

10 голосов
/ 04 августа 2010

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

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

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

РЕДАКТИРОВАТЬ: Чтобы ответить на ваши изменения:

Все, что мне нужно знать, это то, могу ли я рассчитывать на все контексты выполнения, что оба потока (например, Thread1 и Thread2) могут быть в методе одновременно, например, thread2 в первом блоке синхронизации и thread1 ввторой блок синхронизации для достижения параллелизма

Абсолютно нет!Гарантируется, что у не будет двух потоков в синхронизированном блоке, синхронизированных на одном мониторе.

У вас есть три раздела кода: первый синхронизированный блок, несинхронизированная часть ивторая синхронизированная часть.

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

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

3 голосов
/ 06 августа 2010

Все, что мне нужно знать, - могу ли я рассчитывать на то, что оба потока (например, Thread1 и Thread2) могут быть в методе одновременно, например, thread2 в первом блоке синхронизации и thread1 ввторой блок синхронизации для достижения параллелизма

Нет!Это никогда не так.С this связана только одна блокировка.Благодаря использованию одной и той же блокировки для обоих синхронизированных блоков, Thread2 не может находиться в первом блоке synchronized, если Thread1 находится во втором блоке synchronized.

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

3 голосов
/ 04 августа 2010

Нет, это не так. Например, для кода выше

Поток один входит в первый синхронизированный блок, выполняет его и выходит, затем выключается. Второй поток входит в первый блок синхронизации, выполняет его с приращением, а затем входит во второй блок синхронизации перед переключением. Теперь поток 1 не может продолжаться, пока поток 2 не выйдет из второго блока синхронизации.

Этот шаблон не может произойти, если весь метод синхронизирован.

2 голосов
/ 04 августа 2010

Учитывая, что ключевое слово synchronized используется для реализации мониторов в Java, данный фрагмент кода не может быть гарантированно синхронизирован.

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

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

Более подробную информацию можно найти на сайте artima.com в разделе из раздела «Виртуальная машина Java» .

EDIT :

Учитывая тот факт, что вопрос теперь отражает использование локального объекта String, последовательность операций можно рассматривать как потокобезопасную.Каждый поток создает свою собственную локальную ссылку в стеке на объект String;любая мутация объекта в одном потоке не повлияет на другой из-за неизменного свойства объектов String (новый объект String создается для всех практических целей, когда происходит мутация, и, следовательно, состояние не является истинно общим для потоков).

Подчеркивание :

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

1 голос
/ 04 августа 2010

Нет, это не эквивалентно synchronized void doSomething(), потому что i++ не является атомарной операцией. На самом деле, это, вероятно, что-то вроде

int temp = i; i = i + 1; result = temp

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

0 голосов
/ 04 августа 2010

Этот метод не то же самое, что сделать его синхронизированным.Поведение, которое вы объяснили, может быть замечено, но никогда не может быть гарантировано.

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