Java синхронизируется на объекте - PullRequest
4 голосов
/ 21 октября 2011

У меня есть большой вопрос для вас.Как синхронизировать два разных метода из одного и того же класса, чтобы заблокировать один и тот же объект?Вот пример:

public class MyClass extends Thread implements Observer{
  public List<AnotherClass> myList = null;

  public MyClass(List<AnotherClass> myList){
    this.myList = myList;
  }

  public void run(){
    while(true){
       //Do some stuff 
       myList.add(NotImportantElement);
    }
  }

  public void doJob{
    for(int i=0; i<myList.size; i++){
      ElementClass x = myList.get(i);
      //Do some more stuff
    }
  }
}

Вопрос в том, как я могу остановить запуск run () от доступа к myList при выполнении doJob и наоборот?

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

Как мне сделать блокировку?Спасибо!

LE

Хорошо, я понял концепцию блокировки, но теперь у меня есть другой вопрос.

Предположим, у меня есть класс с public static myList и только один экземпляр этого класса.Из этого экземпляра я создаю n экземпляров Thread, которые берут каждый элемент этого списка и делают с ним что-то еще.

Теперь, в определенный момент, myList обновляется.Что происходит с теми потоками, которые уже обрабатывали элементы myList?Как мне заблокировать доступ на myList при его обновлении?

Ответы [ 5 ]

5 голосов
/ 21 октября 2011

ПРИМЕЧАНИЕ. В этом коде предполагается, что у вас есть только один экземпляр MyClass.в соответствии с вашим постом, который звучит как случай.

public class MyClass extends Thread implements Observer{
  private List<AnotherClass> myList = null;
  private Object lock = new Object();

  public MyClass(List<AnotherClass> myList){
    this.myList = new ArrayList(myList);
  }

  public void run(){
    while(true){
       //Do some stuff 
       synchronized(lock) {
        myList.add(NotImportantElement);
       }
    }
  }

  public void doJob{
    synchronized(lock) {
      for(int i=0; i<myList.size; i++){
        ElementClass x = myList.get(i);
        //Do some more stuff
      }
    }
  }
}

РЕДАКТИРОВАТЬ: Добавлено создание копии списка, чтобы внешние объекты не могли изменить список согласно JB Nizet

РЕДАКТИРОВАТЬ 2: Сделал переменные закрытыми, чтобы никто другой не мог получить к ним доступ

4 голосов
/ 21 октября 2011

Вы можете:

  1. Объявить как run, так и doJob synchronized.Это будет использовать this как блокировку;
  2. Объявить список как final и синхронизировать его.Это будет использовать список в качестве блокировки.Объявление поля блокировки как final является хорошей практикой.Таким образом, некоторые методы вашего класса могут синхронизироваться на одном объекте, в то время как другие методы могут использовать другой объект для синхронизации.Это уменьшает конкуренцию за блокировку, но увеличивает сложность кода;
  3. Введите явную переменную java.util.concurrent.locks.Lock и используйте ее методы для синхронизации.Это улучшит гибкость кода, но также увеличит сложность кода;
  4. Не делайте явную синхронизацию вообще, а используйте некоторую поточно-ориентированную структуру данных из JDK.Например, BlockingQueue или CopyOnWriteArrayList.Это уменьшит сложность кода и обеспечит безопасность потока.
  5. Используйте синхронизацию при чтении / записи в поле volatile.Смотрите этот ТАК пост.Это обеспечит безопасность, но значительно увеличит сложность.На второй мысли, не делайте этого:)
1 голос
/ 21 октября 2011

Объявите оба метода как synchronized для блокировки каждого экземпляра или используйте блок synchronized(this){...} для блокировки только текущего экземпляра.

1 голос
/ 21 октября 2011
synchronized(myList) {
    // do stuff on myList
}

Специальная документация: Внутренние блокировки и синхронизация

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

1 голос
/ 21 октября 2011

Вы можете добавить

синхронизируется

ключевое слово для обоих методов ИЛИ использовать

synchronized(Myclass.class) {
}

Первый по сути использует объект Myclass.class, но он не такой мелкозернистый, как последний.

...