Вопрос о поточно-ориентированном коде - PullRequest
0 голосов
/ 03 августа 2011

У меня есть класс следующим образом

public MyClass{

Boolean flag = false;

    public Boolean getflag(){
       synchronized(flag){
          //BLOCK 1
          return flag;
       }
    }

    public Boolean setflag(){
       synchronized(flag){
           //BLOCK 2
           this.flag = flag;
       }
    }
}

Оба метода синхронизированы по объектному флагу. Теперь я сомневаюсь, что два разных потока могут одновременно выполнять синхронизированные блоки (1 и 2).Может ли возникнуть следующая ситуация?1) Поток 1 устанавливает значение флага, а поток 2 получает его значение одновременно?

Ответы [ 4 ]

9 голосов
/ 03 августа 2011

Да, может. Обратите внимание, что вы синхронизируете на том же объекте, который вы устанавливаете . Таким образом, установщик может изменить ссылку на объект на что-то другое, тогда получатель может синхронизироваться на новом объекте, пока установщик все еще находится внутри синхронизированного блока.

Но есть и другое: flag - это (обычно) ссылка на один из общесистемных синглетонов Boolean.TRUE и Boolean.FALSE, поэтому (по крайней мере теоретически) можно заблокировать их вне вашего класса даже без ссылки на ваш класс. В этом случае вы можете зайти в тупик, и вам будет трудно понять, почему.

(Обратите внимание, что код в его текущей форме является неправильным, так как установщик не имеет параметра, поэтому this.flag = flag присваивает ссылку себе - но выше я предполагаю, что вы имели в виду, что он ведет себя как обычный установщик:)

Исправление заключается в использовании выделенного private final объекта блокировки (в случае, если вы хотите абсолютно гарантировать, что никто снаружи не сможет синхронизироваться с той же блокировкой, которую вы используете в своем классе - что, я полагаю, было вашим первоначальным намерением): 1014 *

public MyClass{

    private final Object lock = new Object();
    private Boolean flag = false;

    public Boolean getflag(){
       synchronized(lock){
          //BLOCK 1
          return flag;
       }
    }

    public void setflag(Boolean flag){
       synchronized(lock){
           //BLOCK 2
           this.flag = flag;
       }
    }
}

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

2 голосов
/ 03 августа 2011

Я предполагаю, что ваш метод setFlag должен иметь параметр и не иметь возвращаемого значения?

Мне это кажется плохой идеей.

  • Флаг установлен на ссылку r1
  • Тема 1 вызывает setFlag (r2)
  • Поток 1 получает блокировку на r1
  • Поток 1 устанавливает флаг для ссылки r2
  • Поток 2 получает блокировку на r2
  • Оба потока фактически выполняются одновременно, оба в синхронизированном блоке, но блокируются на разных объектах ...

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

Я бы использовал этот дизайн вместо:

private final Object lock = new Object();
private boolean flag;

public void setFlag(boolean flag) {
    synchronized (lock) {
        this.flag = flag;
    }
}

public boolean getFlag() {
    synchronized (lock) {
        return flag;
    }
}

(Или просто используйте изменяемое поле, потенциально. Это действительно зависит от того, что еще находится в классе.)

0 голосов
/ 03 августа 2011

Это не сработает.Вы блокируете на мониторе объект, на который ссылается флаг.Представьте, что поток входит в установщик, блокирует текущий объект флага, а затем флаг указывает на новый флаг.Затем второй поток входит в геттер и может получить блокировку на флаге, поскольку теперь он указывает на другой объект.

Следовательно, вы можете иметь два потока, оба на первый взгляд заблокированные на «флаг», но на разныеобъекты.Вот почему объекты, используемые для блокировки, как правило, должны быть объявлены как final, чтобы избежать возникновения такой ситуации.

0 голосов
/ 03 августа 2011

Проблема может быть в методе setFlag - он изменит «объект блокировки» для синхронизации.Вы должны быть уверены, что синхронизируете один и тот же объект.
private Object lock = new Object ();И синхронизировать на объекте блокировки.

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