AtomicInteger в Java

Сегодня мы рассмотрим AtomicInteger в Java. Атомарные операции выполняются в единичном блоке задачи без вмешательства других операций. Атомарные операции необходимы в многопоточной среде для избегания несогласованности данных.

AtomicInteger

Создадим простую многопоточную программу, где каждый поток увеличивает общую переменную count 4 раза. Таким образом, если есть два потока, после их завершения значение count должно быть равно 8. JavaAtomic.java

package com.journaldev.concurrency;

public class JavaAtomic {

    public static void main(String[] args) throws InterruptedException {

        ProcessingThread pt = new ProcessingThread();
        Thread t1 = new Thread(pt, "t1");
        t1.start();
        Thread t2 = new Thread(pt, "t2");
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Processing count=" + pt.getCount());
    }

}

class ProcessingThread implements Runnable {
    private int count;

    @Override
    public void run() {
        for (int i = 1; i < 5; i++) {
            processSomething(i);
            count++;
        }
    }

    public int getCount() {
        return this.count;
    }

    private void processSomething(int i) {
        // обработка некоторой работы
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Если вы запустите указанную выше программу, вы заметите, что значение count варьируется от 5 до 8. Причина в том, что count++ не является атомарной операцией. Таким образом, к моменту, когда один поток считывает его значение и увеличивает его на один, другой поток уже считал старое значение, что приводит к неправильному результату. Чтобы решить эту проблему, мы должны убедиться, что операция увеличения счетчика атомарна. Мы можем сделать это, используя Синхронизацию, но в Java 5 java.util.concurrent.atomic предоставляет обёрточные классы для int и long, которые можно использовать для достижения этой атомарной операции без использования Синхронизации.

Пример Java AtomicInteger

Вот обновленная программа, которая всегда будет выводить значение счетчика как 8, потому что метод incrementAndGet() класса AtomicInteger атомарно увеличивает текущее значение на единицу.

package com.journaldev.concurrency;

import java.util.concurrent.atomic.AtomicInteger;

public class JavaAtomic {

    public static void main(String[] args) throws InterruptedException {

        ProcessingThread pt = new ProcessingThread();
        Thread t1 = new Thread(pt, "t1");
        t1.start();
        Thread t2 = new Thread(pt, "t2");
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Processing count=" + pt.getCount());
    }
}

class ProcessingThread implements Runnable {
    private AtomicInteger count = new AtomicInteger();

    @Override
    public void run() {
        for (int i = 1; i < 5; i++) {
            processSomething(i);
            count.incrementAndGet();
        }
    }

    public int getCount() {
        return this.count.get();
    }

    private void processSomething(int i) {
        // обработка некоторой работы
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Преимущества использования классов Concurrency для атомарных операций заключаются в том, что нам не нужно беспокоиться о синхронизации. Это повышает читаемость кода и снижает вероятность ошибок. Кроме того, атомарные операции считаются более эффективными, чем синхронизация, которая включает блокировку ресурсов.

Source:
https://www.digitalocean.com/community/tutorials/atomicinteger-java