Делает ли синхронизация в поле stati c, которое вы изменяете, ваш поток кода безопасным?
Нет, потому что вы переназначаете его. (*)
Как только это переназначение произошло, вы фактически утратили взаимное исключение при доступе к полю staticCode
.
- Любой поток, который уже ожидает в блоке
synchronized
до того, как назначение продолжит ждать. Любой поток, который поступит в блок synchronized
после переназначения, но до того, как переназначенный поток покинул блок, попытается синхронизироваться по новое значение staticCode
.
Более тонкий момент, чем тот факт, что у вас нет взаимного исключения, заключается в том, что вы также теряете случайность перед концом синхронизированного блока. и начало следующего исполнения. Это означает, что у вас нет гарантированной видимости обновленного значения, поэтому вы можете потенциально сгенерировать несколько экземпляров A
с одним и тем же code
.
Это плохая идея синхронизировать на не финальном члене. Если вы не хотите синхронизироваться на A.class
, вы можете определить вспомогательный элемент для синхронизации:
class A {
private static final Object lock = new Object();
private static BigInteger staticCode = BigInteger.ZERO;
public A() {
synchronized (lock) {
staticCode = ...
}
}
}
Это сохраняет изменчивость staticCode
, но допускает корректное взаимное исключение.
Однако класс Atomic*
будет намного проще, потому что вы избегаете необходимости синхронизации (например, AtomicInteger
или AtomicLong
- но если вы действительно думаете, что у вас будет более 2 ^ 63 вещей, Вы можете использовать AtomicReference<BigInteger>
):
class A {
private static final Object lock = new Object();
private static AtomicReference<BigInteger> staticCode = new AtomicReference<>(BigInteger.ZERO);
public A() {
BigInteger code;
do {
code = staticCode.get();
} while (!staticCode.compareAndSet(code, code.add(BigInteger.ONE)));
this.code = code;
// Even easier with AtomicInteger/Long:
// this.code = BigInteger.valueOf(staticCode.incrementAndGet());
}
}
(*) Но в любом случае, не думайте, что синхронизация автоматически делает что-то поточно-безопасным. Во-первых, вам нужно точно определить, что вы подразумеваете под «потокобезопасностью»; но затем вам необходимо понять, что на самом деле обеспечивает синхронизация, чтобы оценить, удовлетворяют ли эти требования вашим требованиям безопасности потоков.