class ThreadSafeClass extends Thread
{
private static int count = 0;
public synchronized static void increment()
{
count++;
}
public synchronized void decrement()
{
count--;
}
}
Может ли кто-нибудь объяснить, почему вышеуказанный класс не является потокобезопасным?
java
multithreading
thread-safety
das kinder
источник
источник
increment
), он будет потокобезопасным. Или если вы использовали какой-то блокирующий объект. Как я уже сказал, я не знаю о Java - мой комментарий основан на знании C #.synchronized
следует использовать только в статических методах. Поэтому, по моему мнению, даже если вы удалитеincrement
метод, он все равно не является потокобезопасным, поскольку два экземпляра (которые имеют только синхронизированный доступ через один и тот же экземпляр) могут вызывать метод одновременно.Ответы:
Поскольку это
increment
метод,static
он будет синхронизироваться с объектом класса дляThreadSafeClass
.decrement
Метод не является статическим и синхронизировать экземпляр используется для его вызова. То есть они будут синхронизироваться на разных объектах, и, таким образом, два разных потока могут выполнять методы одновременно. Так как++
и--
операции не являются атомарными класс не поточно.Кроме того, поскольку
count
isstatic
, изменение его методаdecrement
синхронизированного экземпляра небезопасно, поскольку его можно вызывать в разных экземплярах иcount
одновременно изменять таким образом.источник
count
ISstatic
, имеющий метод экземпляраdecrement()
является неправильным, даже если не было никакогоstatic
increment()
способа, так как два потока может ссылатьсяdecrement()
на разных случаях модифицирующих тот же счетчик одновременно.synchronized
блоки в целом (даже для всего содержимого метода) вместо использования модификатора метода, т.е.synchronized(this) { ... }
иsynchronized(ThreadSafeClass.class) { ... }
.++
и--
не являются атомарными даже наvolatile int
.Synchronized
заботится о проблеме чтения / обновления / записи с помощью++
/--
, ноstatic
ключевое слово здесь является ключевым . Хороший ответ!synchronized
метода экземпляра. Только не ожидайте, что "synchronized" в методе экземпляра обеспечит защиту статических данных. Единственная проблема здесь в том, что вы сказали в своем первом абзаце: методы используют разные блокировки в попытке защитить одни и те же данные, и это, конечно, не обеспечивает никакой защиты.У вас есть два синхронизированных метода, но один из них статический, а другой - нет. При доступе к синхронизированному методу, в зависимости от его типа (статический или нестатический), другой объект будет заблокирован. Для статического метода блокировка будет помещена на объект Class, в то время как для нестатического блока блокировка будет помещена в экземпляр класса, который запускает метод. Поскольку у вас есть два разных заблокированных объекта, у вас может быть два потока, которые одновременно изменяют один и тот же объект.
источник
increment
будучи статическим, синхронизация будет выполняться в самом классе.decrement
будучи не статическим, синхронизация будет выполняться при создании экземпляра объекта, но это ничего не защищает, посколькуcount
статично.Я хотел бы добавить, что для объявления поточно-безопасного счетчика я считаю, что самый простой способ - использовать
AtomicInteger
вместо примитивного int.Позвольте мне перенаправить вас к информации о
java.util.concurrent.atomic
пакете.источник
Другие ответы довольно хорошо объясняют причину. Я просто резюмирую
synchronized
:synchronized
включенfun1
иfun2
синхронизируется на уровне экземпляра объекта.synchronized
onfun4
синхронизируется на уровне объекта класса. Что значит:a1.fun1()
одновременно, последний вызов будет заблокирован.a1.fun1()
и потока 2a1.fun2()
последний вызов будет заблокирован.a1.fun1()
и поток 2 вызываетa1.fun3()
одновременно, без блокировки, 2 метода будут выполняться одновременно.A.fun4()
, если другие потоки называютA.fun4()
илиA.fun5()
в то же время, последние вызовы будут заблокированы , так какsynchronized
на одномfun4
уровне класса.A.fun4()
потока 2a1.fun1()
одновременно, без блокировки, 2 метода будут выполняться одновременно.источник
decrement
блокирует другой объект,increment
чтобы они не мешали друг другу работать.decrement
одного экземпляра блокирует другое, чем вызовdecrement
другого экземпляра, но они влияют на одно и то же.Первый означает, что перекрывающиеся вызовы
increment
иdecrement
могут привести к отмене (правильной), увеличению или уменьшению.Второе означает, что два перекрывающихся вызова в
decrement
разных экземплярах могут привести к двойному (правильному) или единственному уменьшению.источник
Поскольку два разных метода, один - это уровень экземпляра, а другой - уровень класса, поэтому вам нужно заблокировать 2 разных объекта, чтобы сделать его ThreadSafe
источник
Как объясняется в других ответах, ваш код не является потокобезопасным, поскольку статический метод
increment()
блокирует монитор классов, а нестатический методdecrement()
блокирует монитор объектов.Для этого примера кода существует лучшее решение без
synchronzed
использования ключевых слов. Вы должны использовать AtomicInteger для обеспечения безопасности потоков.Потокобезопасное использование
AtomicInteger
:источник