Подтвердить что ты не робот

Создание класса Thread-Safe

Дано:

public class TestSeven extends Thread {

private static int x;

public synchronized void doThings() {
    int current = x;
    current++;
    x = current;
}

public void run() {
    doThings();
  }
}

Какое утверждение верно?

а. Ошибка компиляции.

В. Исключение создается во время выполнения.

С. Синхронизация метода run() сделает класс потокобезопасным.

Д. Данные в переменной "x" защищены от одновременных проблем доступа.

Е. Объявление метода doThings() как static сделает класс потокобезопасным.

F. Обертка операторов в doThings() в синхронизированном (новый объект()) {} блок сделает класс потокобезопасным.

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

4b9b3361

Ответ 1

Е. Объявление метода doThings() как static сделает класс потокобезопасным.

Это своего рода сложный ответ. Метод уже синхронизирован, но в экземпляре, тогда как состояние находится в статическом поле, то есть в классе. Сделать его static synchronized действительно правильным ответом, потому что тогда он синхронизируется с классом, а не на (бессмысленном) экземпляре.

Д. Данные в переменной "x" защищены от одновременных проблем доступа.

private static int x;

Это статическая переменная. Он разделяется всеми экземплярами класса, поэтому синхронизация в отдельных экземплярах не помогает, так же, как F не будет полезен, что синхронизируется с полным удаленным фиктивным объектом.

Ответ 2

В соответствии с спецификацией языка:

Синхронизированный метод получает монитор (§17.1) перед его выполнением.

Для класса (статического) метода монитор, связанный с классом объект для класса метода.

Для метода экземпляра монитор, связанный с этим (объект для которого был вызван метод).

Это означает, что в коде, который вы предоставили, ключевое слово synchronized заставляет метод получать блокировку на this перед тем, как выполнить тело метода. Однако, поскольку x - static, который не гарантирует, что обновление до x будет атомарным. (Другой экземпляр класса может войти в синхронную область и выполнить обновление одновременно, так как они имеют разные значения this и, следовательно, различные блокировки.)

Однако объявление doStuff static приведет к тому, что все вызовы метода получат одну и ту же блокировку (тот, что находится на Class), и таким образом обеспечит взаимное исключение в теле метода.

В спецификациях действительно указано, что:

class A {
    static synchronized void doSomething() {
        // ...
    }
}

- буквально то же самое, что

class A {
    static void doSomething() {
        synchronized(A.class) {
            // ...
        }
    }
}

Аналогично:

class B {
    synchronized void doSomething() {
        // ...
    }
}

- буквально то же самое, что

class B {
    void doSomething() {
        synchronized (this) {
            // ...
        }
    }
}

Ответ 3

В целях синхронизации метода doThings() вы удерживаете блокировку для определенного объекта TestSeven. Однако статические переменные класса не относятся к конкретному экземпляру самого объекта. Они принадлежат объекту Class TestSeven.class. Таким образом, вы можете пойти на

synchronized (TestSeven.class){
    int current = x;
    current++;
    x = current;
}

внутри метода doThings(), который получает блокировку класса внутри блокировки экземпляра, которая переусердствует. Таким образом, вы можете пометить метод как статический, чтобы вы в конечном итоге приобрели блокировку объекта класса.

Ответ 4

Так как x является static, другие потоки могут изменять его одновременно с тем, как работает метод doThings. Создание doThings static остановит это.

Ответ 5

Я согласен с вами в том, что правильный ответ - D. Я бы сказал, что E неверно, потому что, если я устанавливаю doThings() как статический и удаляю синхронизированное ключевое слово, я мог бы просто запустить 50 потоков TestSeven, и это может привести к неправильному значению x.

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