Почему этот класс не является потокобезопасным?
class ThreadSafeClass extends Thread
{
private static int count = 0;
public synchronized static void increment()
{
count++;
}
public synchronized void decrement()
{
count--;
}
}
Может ли кто-нибудь объяснить, почему выше класс не является потокобезопасным?
Ответы
Ответ 1
Так как метод increment
static
, он будет синхронизироваться с объектом класса для ThreadSafeClass
. Метод decrement
не является статическим и будет синхронизироваться в экземпляре, используемом для его вызова. I.e., они будут синхронизироваться на разных объектах, и, таким образом, два разных потока могут одновременно выполнять методы. Поскольку операции ++
и --
не являются атомарными, класс не является потокобезопасным.
Кроме того, поскольку count
является static
, его изменение из decrement
, которое является синхронным методом экземпляра, является небезопасным, поскольку оно может быть вызвано в разных экземплярах и одновременно изменить count
таким образом.
Ответ 2
У вас есть два синхронизированных метода, но один из них является статическим, а другой - нет. При доступе к синхронизированному методу, на основе его типа (статического или нестатического), другой объект будет заблокирован. Для статического метода блокировка будет помещаться в объект класса, а для нестатического блока - блокировка будет помещена в экземпляр класса, который запускает метод. Поскольку у вас есть два разных заблокированных объекта, вы можете иметь два потока, которые одновременно изменяют один и тот же объект.
Ответ 3
Может ли кто-нибудь объяснить, почему выше класс не является потокобезопасным?
-
increment
является статическим, синхронизация будет выполняться в самом классе.
-
decrement
не является статическим, синхронизация будет выполняться при создании экземпляра объекта, но это не гарантирует ничего, поскольку count
является статическим.
Я хотел бы добавить, что для объявления потокобезопасного счетчика я считаю, что самый простой способ - использовать AtomicInteger
вместо примитивного int.
Позвольте мне перенаправить вас в java.util.concurrent.atomic
package-info.
Ответ 4
-
decrement
блокирует другую вещь до increment
, поэтому они не мешают друг другу работать.
- Вызов
decrement
в одном экземпляре блокирует другую вещь для вызова decrement
в другом экземпляре, но они влияют на одно и то же.
Первое означает, что перекрывающиеся вызовы increment
и decrement
могут привести к отмене (правильному), приращению или декрету.
Второе означает, что два перекрывающих вызова на decrement
в разных экземплярах могут приводить к двойному декременту (правильному) или одному декрету.
Ответ 5
Ответы других довольно хорошо объясняют причину. Я просто добавляю что-то, чтобы суммировать synchronized
:
public class A {
public synchronized void fun1() {}
public synchronized void fun2() {}
public void fun3() {}
public static synchronized void fun4() {}
public static void fun5() {}
}
A a1 = new A();
synchronized
на fun1
и fun2
синхронизируется на уровне объекта экземпляра. synchronized
on fun4
синхронизируется на уровне объектов класса. Это означает:
- Когда 2 потока вызывают
a1.fun1()
в то же время, последний вызов будет заблокирован.
- Когда поток 1 вызывает
a1.fun1()
и поток 2 вызывает a1.fun2()
в то же время, последний вызов будет заблокирован.
- Когда поток 1 вызывает
a1.fun1()
и поток 2 вызывает a1.fun3()
в одно и то же время, без блокировки, два метода будут выполняться в одно и то же время.
- Когда поток 1 вызывает
A.fun4()
, если другие потоки вызывают A.fun4()
или A.fun5()
в то же время, последние вызовы будут заблокированы, так как synchronized
on fun4
является уровнем класса.
- Когда поток 1 вызывает
A.fun4()
, поток 2 вызывает a1.fun1()
в то же время, без блокировки, два метода будут выполняться в одно и то же время.
Ответ 6
Поскольку два разных метода: один - это уровень экземпляра, а другой - уровень класса, поэтому вам нужно заблокировать 2 разных объекта, чтобы сделать его ThreadSafe
Ответ 7
Как объясняется в других ответах, ваш код не защищен потоком, поскольку статический метод increment()
блокирует контроль класса и нестатический метод decrement()
блокирует монитор объекта.
В этом примере кода лучшее решение существует без использования ключевого слова synchronzed
.
Вы должны использовать AtomicInteger для обеспечения безопасности Thread.
Потоковая защита с помощью AtomicInteger
:
import java.util.concurrent.atomic.AtomicInteger;
class ThreadSafeClass extends Thread {
private static AtomicInteger count = new AtomicInteger(0);
public static void increment() {
count.incrementAndGet();
}
public static void decrement() {
count.decrementAndGet();
}
public static int value() {
return count.get();
}
}