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

Это связано с добавлением дополнений, чтобы удовлетворить ограничениям выравнивания. Уравнение структуры данных влияет как на производительность, так и на правильность программ:

  • Недопустимый доступ может быть жесткой ошибкой (часто SIGBUS).
  • Неравномерный доступ может быть мягкой ошибкой. Либо исправлено в аппаратном обеспечении, либо для снижения производительности. Или исправлено путем эмуляции в программном обеспечении, для серьезной деградации производительности. Кроме того, атомарность и другие гарантии параллелизма могут быть нарушены, что приводит к незначительным ошибкам.

Вот пример использования типичных настроек для процессора x86 (все используемые 32 и 64-битные режимы):

struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

Можно минимизировать размер структур путем сортировки (например, структура Z в приведенном выше примере).

ВАЖНОЕ ЗАМЕЧАНИЕ: В стандартах C и C ++ указано, что выравнивание структуры определяется реализацией , Поэтому каждый компилятор может выбрать выравнивание данных по-разному, что приводит к разным и несовместимым макетам данных. По этой причине при работе с библиотеками, которые будут использоваться разными компиляторами, важно понять, как компиляторы выравнивают данные. Некоторые компиляторы имеют параметры командной строки и / или специальные #pragma операторы для изменения настроек выравнивания структуры.

10
задан Tall Jeff 23 September 2008 в 00:29
поделиться

6 ответов

Если Вы используете, 'синхронизировался' на методе set здесь также, этот код ориентирован на многопотоковое исполнение. Однако это не может быть достаточно детализировано; если у Вас есть 20 методов считывания и методы set, и они все синхронизируются, можно создавать узкое место синхронизации.

В этом определенном экземпляре, с единственной международной переменной, затем устраняя 'синхронизируемый' и отмечая международное 'энергозависимое' поле также гарантирует видимость (каждый поток будет видеть последнее значение 'val' при вызове метода считывания), но это не может синхронизироваться достаточно для потребностей. Например, ожидание

 int old = someThing.getVal();
 if (old == 1) {
    someThing.setVal(2);
 }

установить val на 2, если и только если это уже 1, является неправильным. Для этого Вам нужна внешняя блокировка или некоторый атомарный compare-set.

Я настоятельно рекомендую, что Вы читаете Параллелизм Java На практике Brian Goetz и др., он имеет лучшее покрытие конструкций параллелизма Java.

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

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

Править: Вот ссылка еще на некоторую информацию о синхронизации и что нет.

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

В дополнение к комментарию Кауэна Вы могли сделать следующее для сравнивания и хранилища:

synchronized(someThing) {
    int old = someThing.getVal();
    if (old == 1) {
        someThing.setVal(2);
    }
}

Это работает, потому что блокировка, определенная через синхронизированный метод, является неявно тем же как блокировкой объекта (см. спецификацию языка Java).

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

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

Пример:

public class SomeClass {

  private Object mutex = new Object();
  private int    val   = -1; // TODO: Adjust initialization to a reasonable start
                             //       value
  public int getVal() {
    synchronized ( mutex ) {
      return val;
    }
  }

  private void setVal( int val ) {
    synchronized ( mutex ) {
      this.val = val;
    }
  }
}

Гарантирует что только одно чтение потока или записи к локальному члену экземпляра.

Прочитайте книгу "Параллельное Программирование в Java (TM): Принципы разработки и Шаблоны (Java (Addison-Wesley))", возможно, http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html также полезен...

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

Синхронизация существует для защиты от ошибок непротиворечивости интерференции и памяти потока. Путем синхронизации на getVal (), код гарантирует, что другие синхронизированные методы для SomeClass также не выполняются одновременно. С тех пор нет никаких других синхронизированных методов, это не обеспечивает много значения. Также обратите внимание, что чтения и записи на примитивах имеют атомарный доступ. Это означает с тщательным программированием, не нужно синхронизировать доступ к полю.

Read Sychronization.

Едва ли уверенный, почему это было отброшено к-3. Я просто суммирую то, что учебное руководство по Синхронизации от Sun говорит (а также мой собственный опыт).

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

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

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

public class ThreadSafeSomeClass {

    private final AtomicInteger value = new AtomicInteger(0);

    public void setValue(int x){
         value.set(x);
    }

    public int getValue(){
         return value.get();
    }

}

Однако, если вы добавляете дополнительные переменные, которые становятся зависимыми (состояние одной переменной зависит от состояние другого), то AtomicInteger работать не будет.

Повторяя предложение прочитать «Java Concurrency in Practice».

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

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