Могат ли синхронизираните блокове да бъдат по-бързи от Atomics?

Да предположим две следните реализации на брояча:

class Counter {
  private final AtomicInteger atomic = new AtomicInteger(0);
  private int i = 0;

  public void incrementAtomic() {
    atomic.incrementAndGet();
  }

  public synchronized void increment() {
    i++;
  }
}

На пръв поглед atomics трябва да бъде по-бърз и по-мащабируем. И те са, вярвам. Но по-бързи ли са от synchronized блока през цялото време? Или съществуват някои ситуации, когато това правило е нарушено (напр. SMP/машина с един процесор, различен CPU ISA, операционни системи и т.н.)?


person Denis Bazhenov    schedule 18.07.2012    source източник


Отговори (5)


incrementAndGet може да се реализира като CAS-цикъл. В ситуации на силно удовлетворение това може да доведе до неуспех на n-1 от вашите нишки, което да доведе до O(n) проблем за n нишки.

( За @Geek:

Обикновено getAndIncrement може да се реализира нещо като:

 int old;
 do {
     old = value;
 } while (!compareAndSet(value, old, old+1));
 return old;

Представете си, че имате n нишки, изпълняващи този код на един и същ атом и те са в крак една с друга. Първата итерация върши kn работа. Само един CAS ще успее. Другите n-1 нишки ще повтарят упражнението, докато остане само една. Така че общата работа е O(n^2) (най-лошият случай) вместо O(n). )

Като каза това, придобиването на ключалка ще трябва да направи нещо подобно в най-добрия случай, а ключалките не са в най-добрия си вид, когато се борят силно. Едва ли ще видите голямо предимство за ключалките, докато не използвате CAS-цикъл, който изисква значителни изчисления преди get and compareAndSwap.

person Tom Hawtin - tackline    schedule 19.07.2012
comment
можете ли да разясните по-подробно първия си параграф? - person Geek; 19.07.2012

Или съществуват някои ситуации, когато това правило е нарушено (напр. SMP/машина с един процесор, различен CPU ISA, операционни системи и т.н.)?

Не знам за такива. (И аз съм готов да бъда коригиран ... ако някой знае конкретен контрапример.)

Въпреки това (и това е основната ми гледна точка) няма теоретична причина, поради която не можете да имате хардуерна архитектура или лошо внедрена JVM, в която synchronized е със същата скорост или по-бърза от atomic типовете. (Относителната скорост на тези две форми на синхронизация е проблем на имплементацията и като такава може да бъде количествено определена само за съществуващи реализации.)

И разбира се, това не означава, че никога не трябва да използвате synchronized. Конструкцията synchronized има много случаи на употреба, които атомарните класове не адресират.

person Stephen C    schedule 19.07.2012
comment
Да, атомиците гарантират видимост на паметта, но не и взаимно изпълнение. - person Denis Bazhenov; 19.07.2012

Зависи от внедряването - така че в крайна сметка ще трябва да направите сравнителен анализ на вашата конкретна платформа / JVM / конфигурация.

Като каза това, atomics би трябвало винаги да е по-бързо поради следните причини:

  • Atomics са проектирани така, че JVM да може да се възползва от атомните машинни инструкции, които са най-бързите атомни операции, които можете да получите за повечето платформи
  • synchronized използва сравнително тежки схеми за заключване с наблюдаващи обекти, които са предназначени да защитават потенциално големи блокове код. Тази форма на заключване по своята същност е по-сложна от атомарните операции, така че бихте очаквали да има по-високи разходи за изпълнение.
person mikera    schedule 19.07.2012

Както казаха други, това зависи от изпълнението. Но имайте предвид, че ако вашите програмни инварианти включват повече от една променлива, тогава трябва да използвате синхронизация, за да ги актуализирате заедно. Не можете да извършвате атомарна операция върху две свързани променливи заедно само защото са от тип Atomic. В този случай единственият ви приятел е синхронизиран.

person Geek    schedule 19.07.2012

атомните променливи винаги ще бъдат по-бързи.

можете да видите, че пакетът java.util.concurrent винаги използва атомарни променливи, а не синхронизирани блокове.

person tommo    schedule 18.07.2012
comment
Второто твърдение, макар и вярно, не доказва нищо. Всъщност това само подразбира, че атомните типове са по-бързи средно ... за тези конкретни случаи на употреба ... на ISA / платформите, които Oracle поддържа. - person Stephen C; 19.07.2012
comment
Защо? Ако второто ви изречение трябва да предостави причината, това не е така. Посочената цел на паралелния пакет е да не използва синхронизация навсякъде. - person user207421; 19.07.2012
comment
в много малко случаи на оптимистично заключване атомните променливи са по-бавни и причината, която споменах, беше да покажа, че те са предпочитано решение и вероятно са по-доброто решение в повечето случаи. - person tommo; 19.07.2012
comment
Все още не сте казали защо; и това, което паралелният пакет прави вътрешно, не е доказателство за производителност; той само показва, че паралелният пакет отговаря на своите дизайнерски цели. Би било удивително, ако не използва свои собствени класове. - person user207421; 19.07.2012