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

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

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

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

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

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

Обновление:

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

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

У меня есть 5 экземпляров что Foo с 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 экземпляров этого объекта, которые получают доступ к этому коду байта, поэтому если я хочу препятствовать тому, чтобы они наложились на том же коде байта, что я должен сделать?

Спасибо, Adam.

11
задан TacB0sS 23 June 2010 в 05:00
поделиться

4 ответа

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

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

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

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

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

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

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

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

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

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

11
ответ дан 3 December 2019 в 07:36
поделиться

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

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

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

3
ответ дан 3 December 2019 в 07:36
поделиться

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

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 являются атомарными и реализованы с использованием аппаратных операций. Поэтому они обеспечивают лучшую производительность.

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();
  }
}
2
ответ дан 3 December 2019 в 07:36
поделиться

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

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

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

0
ответ дан 3 December 2019 в 07:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: