Многопоточность на разных экземплярах одного и того же объекта в Java - PullRequest
11 голосов
/ 23 июня 2010

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

1 нить -> 1 экземпляр - класса Foo == без проблем.

X threads -> 1 экземпляр - класса Foo == необходимо обрабатывать, это понятно.

X потоки -> X соответствующих экземпляров - класса Foo == ????

я должен сделатьуверен, что ничего не перепутано в методе?если метод использует переменные уровня instance , могу ли я быть уверен, что он будет использовать правильные переменные?

Обновление:

Я вижу свой вопросНекоторым было непонятно, вот пример с числами

У меня есть объект типа Foo , у которого нет синхронизации !!

У меня есть 5 экземпляров этого Foo с 5-ю потоками, запущенными для / в каждом из них, и доступом к параметрам уровня instance , например:

class FOO {
     private SomeObject someObject=new SomeObject();

     private void problematicMethod(Data data) {
         someObject.doSomethingWithTheData(data);
         data.doSomethingWithSomeObject(someObject); 
// any way you want it use the data or export the data
     }
}

Я спрашиваю, есть ли здесь проблемапоскольку существует только 1 байт-код этого класса и 5 экземпляров этого объекта, которые обращаются к этому байт-коду, поэтому, если я хочу предотвратить их наложение на один и тот же байт-код,что мне делать?

Спасибо, Адам.

Ответы [ 5 ]

11 голосов
/ 23 июня 2010

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

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

1 поток -> 1 экземпляр - класса Test, без проблем

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

X потоков -> 1 экземпляр - класса Test, необходимо обрабатывать это ясно.

Что ж, да, по причинам видимости потока и для обеспечения того, чтобы критические области выполнялись атомарно, использование методов синхронизации или блокировки довольно важно.Конечно, все зависит правильно ?!Если ваш класс не имеет состояния (экземпляра или переменных класса), то вам не нужно делать его поточно-ориентированным (подумайте о служебном классе, таком как Executors, Arrays, Collections классы Java).

X потоков -> X соответствующих экземпляров - класса Test, ????

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

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

Переменные класса ARE static переменные в Java.Уже опубликовано два ответа, в которых подчеркивается важность использования synchronized для предотвращения одновременного изменения переменной класса несколькими потоками.Не упоминается важность использования synchronized или volatile, чтобы убедиться, что вы не видите устаревшую версию переменной класса.

3 голосов
/ 23 июня 2010

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

public class Foo {
  private static int someNumber = 0;
  // not thread safe
  public void inc_unsafe()  { 
    someNumber++;
  }

  // not thread safe either; we are sync'ing here on an INSTANCE of
  // the Foo class
  public synchronized void inc_also_unsafe()  { 
    someNumber++;
  }

  // here we are safe, because for static methods, synchronized will use the class
  // itself
  public static synchronized void inc_safe()  { 
    someNumber++;
  }

  // also safe, since we use the class itself
  public static synchronized void inc_also_safe()  { 
    synchronized (Foo.class) {
      someNumber++;
    }
  }
}

Обратите внимание, что {{java.util.concurrent}} предоставляет гораздо больше способов защиты общих данных, чем ключевое слово {{synchronized}} в Java. Посмотрите на это, как это может быть то, что вы хотите.

( Для тех, кто хочет заявить, что приращение int уже поточно-ориентированное, обратите внимание, что это всего лишь пример, и базовая концепция может применяться ко всему. )

2 голосов
/ 23 июня 2010

Добавление к ответу Дейва , если у вас есть несколько статических методов, работающих на разных статических элементах, лучше синхронизировать на отдельных статических объектах.

public class Foo {
  private static int someNumber = 0;
  private static int otherNumber = 0;
  private static final Object lock1 = new Object();
  private static final Object lock2 = new Object();

  public static void incSomeNumber() {
    synchronized (lock1) {
      someNumber++;
    }
  }

  public static void incOtherNumber() {
    synchronized (lock2) {
      otherNumber++;
    }
  }
}

Таким образом,два разных потока могут одновременно вызывать incSomeNumber и incOtherNumber, не застревая при синхронизации.


EDIT

Вот тот же пример сAtomicInterger.Обратите внимание, что явной блокировки не требуется.Все операции на AtomicInterger s являются атомарными и реализуются с использованием аппаратных операций.Таким образом, они приводят к лучшей производительности.

import java.util.concurrent.atomic.AtomicInteger;

public class Foo {
  private static AtomicInteger someNumber = new AtomicInteger(0);
  private static AtomicInteger otherNumber = new AtomicInteger(0);

  public static int incSomeNumber() {
    return someNumber.incrementAndGet();
  }

  public static int incOtherNumber() {
    return otherNumber.incrementAndGet();
  }
}
0 голосов
/ 31 августа 2013

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

0 голосов
/ 23 июня 2010

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

а) Вы написали свой собственный код, который использовал магию загрузчика классов
б) Вы сделали что-то странное, например, включили свой код как внутри войны, так и внутри общей папки lib tomcat ... и выполнили правильную последовательность событий, чтобы загрузить 2 копии из разных мест.

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

...