Автор оригинала: Pankaj Kumar.
Сегодня мы рассмотрим AtomicInteger
на Java. Атомарные операции выполняются в одной единице задачи без вмешательства других операций. Атомарные операции необходимы в многопоточной среде, чтобы избежать несогласованности данных.
Атомный регистратор
Давайте создадим простую многопоточную программу, в которой каждый поток увеличивает общую переменную count
в 4 раза. Поэтому, если есть два потока, после их завершения количество
значение должно быть 8.
Давайте создадим простую многопоточную программу, в которой каждый поток увеличивает общую переменную || count || в 4 раза. Поэтому, если есть два потока, после их завершения || количество || значение должно быть 8.
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) { // processing some job try { Thread.sleep(i * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
Если вы запустите вышеуказанную программу, вы заметите, что значение count
варьируется в пределах 5,6,7,8. Причина в том, что count++ не является атомной операцией. Таким образом, к тому времени, когда один поток прочитает его значение и увеличит его на единицу, другой поток прочитает более старое значение, что приведет к неправильному результату.
Чтобы решить эту проблему, нам нужно будет убедиться, что операция приращения при подсчете является атомарной, мы можем сделать это с помощью Синхронизации , но Java 5 java.util.concurrent.atomic
предоставляет классы-оболочки для int и long, которые можно использовать для выполнения этой атомной операции без использования синхронизации.
Пример Java AtomicInteger
Вот обновленная программа, которая всегда будет выводить значение счетчика как 8, потому что AtomicInteger
метод incrementAndGet()
атомарно увеличивает текущее значение на единицу.
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) { // processing some job try { Thread.sleep(i * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
Преимущества использования классов параллелизма для атомарной работы заключаются в том, что нам не нужно беспокоиться о синхронизации. Это улучшает читаемость кода и снижает вероятность ошибок. Также предполагается, что классы параллелизма атомарных операций более эффективны, чем синхронизация, которая включает блокировку ресурсов.