Prasyarat kanggo munculé operasi atom

Ayo goleki conto iki kanggo mbantu sampeyan ngerti cara operasi atom:

public class Counter {
    int count;

    public void increment() {
        count++;
    }
}

Yen kita duwe siji utas, kabeh bisa dianggo kanthi apik, nanging yen kita nambah multithreading, kita entuk asil sing salah, lan kabeh amarga operasi tambahan ora mung siji operasi, nanging telu: panjaluk kanggo entuk nilai saiki.ngetang, banjur tambah 1 lan tulis maneh menyangngetang.

Lan nalika rong utas pengin nambah variabel, sampeyan bakal kelangan data. Sing, loro Utas nampa 100, minangka asil, loro bakal nulis 101 tinimbang nilai samesthine 102.

Lan carane ngatasi? Sampeyan kudu nggunakake kunci. Tembung kunci sing disinkronake mbantu ngatasi masalah iki, kanthi nggunakake menehi jaminan manawa siji utas bakal ngakses metode kasebut sekaligus.

public class SynchronizedCounterWithLock {
    private volatile int count;

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

Kajaba iku, sampeyan kudu nambah tembung kunci sing molah malih , sing njamin visibilitas referensi sing bener ing antarane benang. Kita wis nliti karyane ing ndhuwur.

Nanging isih ana kekurangane. Sing paling gedhe yaiku kinerja, ing wektu kasebut nalika akeh benang nyoba entuk kunci lan siji entuk kesempatan nulis, benang liyane bakal diblokir utawa digantung nganti benang dibebasake.

Kabeh proses kasebut, pamblokiran, ngalih menyang status liyane larang banget kanggo kinerja sistem.

Operasi atom

Algoritma kasebut nggunakake instruksi mesin tingkat rendah kayata comparison-and-swap (CAS, compare-and-swap, sing njamin integritas data lan wis ana riset akeh).

Operasi CAS sing khas ngoperasikake telung operan:

  • Ruang memori kanggo kerja (M)
  • Nilai samesthine (A) saka variabel
  • Nilai anyar (B) kanggo disetel

CAS atom nganyari M kanggo B, nanging mung yen nilai M padha karo A, digunakake ora tumindak dijupuk.

Ing kasus pisanan lan kaloro, nilai M bakal dibalekake. Iki ngidini sampeyan nggabungake telung langkah, yaiku, entuk nilai, mbandhingake nilai, lan nganyari. Lan kabeh dadi siji operasi ing tingkat mesin.

Nalika aplikasi multi-threaded ngakses variabel lan nyoba nganyari lan CAS ditrapake, banjur salah sawijining utas bakal entuk lan bisa nganyari. Nanging ora kaya kunci, benang liyane mung bakal entuk kesalahan babagan ora bisa nganyari nilai kasebut. Banjur padha bakal pindhah menyang karya luwih, lan ngoper rampung tilar ing jinis karya.

Ing kasus iki, logika dadi luwih angel amarga kita kudu nangani kahanan nalika operasi CAS ora kasil. Kita mung bakal model kode supaya ora nerusake nganti operasi kasil.

Pambuka kanggo Jinis Atom

Apa sampeyan nemokake kahanan sing kudu nyiyapake sinkronisasi kanggo variabel paling gampang saka jinis int ?

Cara pisanan sing wis kita bahas yaiku nggunakake volatile + sinkronisasi . Nanging ana uga kelas Atomic * khusus.

Yen nggunakake CAS, operasi bisa luwih cepet dibandhingake karo cara pisanan. Kajaba iku, kita duwe cara khusus lan trep banget kanggo nambah nilai lan operasi nambah lan nyuda.

AtomicBoolean , AtomicInteger , AtomicLong , AtomicIntegerArray , AtomicLongArray iku kelas sing operasine atom. Ing ngisor iki kita bakal nganalisa karya karo dheweke.

AtomicInteger

Kelas AtomicInteger nyedhiyakake operasi ing nilai int sing bisa diwaca lan ditulis kanthi atom, saliyane nyedhiyakake operasi atom lengkap.

Wis entuk lan nyetel metode sing bisa digunakake kaya variabel maca lan nulis.

Sing, "kedadeyan-sadurunge" karo sembarang panrimo sakteruse saka variabel padha sing kita ngomong bab sadurungé. Cara atom compareAndSet uga nduweni fitur konsistensi memori kasebut.

Kabeh operasi sing ngasilake nilai anyar ditindakake kanthi atom:

int addAndGet (int delta) Nambahake nilai tartamtu menyang nilai saiki.
boolean compareAndSet(expected int, update int) Nyetel nilai kanggo nilai dianyari diwenehi yen nilai saiki cocog Nilai samesthine.
int decrementAndGet() Ngurangi nilai saiki kanthi siji.
int getAndAdd(int delta) Nambahake nilai sing diwenehake menyang nilai saiki.
int getAndDecrement() Ngurangi nilai saiki kanthi siji.
int getAndIncrement() Nambah nilai saiki kanthi siji.
int getAndSet(int newValue) Nyetel nilai sing diwenehake lan ngasilake nilai sing lawas.
int incrementAndGet() Nambah nilai saiki kanthi siji.
lazySet(int newValue) Pungkasan disetel menyang nilai sing diwenehake.
boolean weakCompareAndSet(expected, update int) Nyetel nilai kanggo nilai dianyari diwenehi yen nilai saiki cocog Nilai samesthine.

Tuladha:

ExecutorService executor = Executors.newFixedThreadPool(5);
IntStream.range(0, 50).forEach(i -> executor.submit(atomicInteger::incrementAndGet));
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.HOURS);

System.out.println(atomicInteger.get()); // prints 50