Динамическая синхронизированная комбинация для производительности
Когда используется Синхронизация, это влияет на производительность. Может ли volatile использоваться в сочетании с синхронизацией, чтобы снизить накладные расходы на производительность? Например, экземпляр счетчика будет разделяться между многими потоками, и каждый поток может обращаться к общедоступным методам Counter. В приведенном ниже коде volatile используется для getter, и синхронизация используется для setter
public class Counter
{
private volatile int count;
public Counter()
{
count = 0;
}
public int getCount()
{
return count;
}
public synchronized void increment()
{
++count;
}
}
Пожалуйста, дайте мне знать, в каком сценарии это может сломаться?
Ответы
Ответ 1
Да, вы определенно можете. На самом деле, если вы посмотрите на исходный код AtomicInteger
, это, в сущности, то, что они делают. AtomicInteger.get
просто возвращает value
, который является ссылкой volatile int
(). Единственное реальное отличие от того, что вы сделали и что они делают, это то, что они используют CAS для приращения вместо синхронизации. На современном оборудовании CAS может устранить любое взаимное исключение; на более старом оборудовании, JVM поместит какой-то мьютекс вокруг приращения.
Неустойчивые чтения примерно так же быстро, как и энергонезависимые, поэтому чтение будет довольно быстрым.
Не только это, но volatile
поля гарантированно не разрываются: см. JLS 17.7, в котором указано, что volatile
long
и double
не подлежат разрыву слова. Таким образом, ваш код будет работать с long
так же, как и int
.
Как указывает Диего Френер, вы можете не увидеть результат приращения, если вы получите значение "прямо как" приращение, вы увидите либо до, либо после. Конечно, если get
были синхронизированы, у вас было бы точно такое же поведение из прочитанного потока - вы либо увидите значение до-инкремента или пост-приращения. Так что это действительно так же в любом случае. Другими словами, не имеет смысла говорить, что вы не увидите значения, как это происходит, если только вы не имеете в виду разрывание слова, которое (а) вы не получите, и (б) вы никогда не захотите.
Ответ 2
1. Я лично использовал этот механизм volatile
в сочетании с synchronized
.
2. Вы можете использовать synchronized
, а , вы всегда получите согласованный результат, но используя только volatile
не будет всегда давать одинаковый результат.
3. Это связано с тем, что ключевое слово volatile не является примитивом синхронизации. Он просто предотвращает кеширование значения в потоке, но он не мешает двум нити изменять одно и то же значение и записывать его одновременно.
4. volatile
предоставляют одновременный доступ к потокам без блокировки, но с помощью synchronized
разрешает только один поток, чтобы получить доступ к этому и все синхронизированные методы в классе.
5. И , используя как volatile and synchronized
, сделает это....
volatile
- будет отображать измененные значения в потоке и предотвращать кеширование,
synchronized
- Но используя синхронизированное ключевое слово, убедитесь, что только один поток получает доступ к синхронизированным методам класса.
Ответ 3
При вызове getCount() вы не всегда будете получать наиболее актуальный счет. AtomicInteger может быть подходящим для вас.
Ответ 4
От использования обоих не будет повышения производительности. Volatile гарантирует, что значение переменной будет согласованным при чтении/записи переменной по потокам, выполняемой параллельно, предотвращая кеширование. Synchronized, когда применяется к методу (как и в вашем примере), позволяет только одному потоку вводить этот метод за раз и блокирует другие пока выполнение не будет завершено.