Многопоточность на разных экземплярах одного и того же объекта в Java

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

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

X темы -> 1 экземпляр - класса Фу == необходимо обработать это четкий.

X темы -> X соответственно экземпляры - класса Фу ==????

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

обновление:

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

Я объект класса type Фу что нет синхронизации!!

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

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 ответов


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

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

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

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

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

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

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

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

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

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


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

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.утиль.concurrent}} предоставляет гораздо больше способов защиты общих данных, чем ключевое слово {{synchronized}} Java. Посмотрим, как это может быть то, что вы хотите.

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


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

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 в то же время, не застревая на синхронизации.


редактировать

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

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();
  }
}

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

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

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


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