AtomicInteger في جافا

اليوم سنتعرف على AtomicInteger في جافا. تُنفذ العمليات الذرية في وحدة مفردة من المهمة دون تداخل من العمليات الأخرى. العمليات الذرية ضرورية في البيئة متعددة الخيوط لتجنب عدم اتساق البيانات.

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 و 6 و 7 و 8. السبب في ذلك هو لأن count++ ليست عملية ذرية. لذلك بحلول الوقت الذي يقرأ فيه أحد الخيوط قيمته ويزيد منها بواحد، قرأ الخيط الآخر القيمة القديمة مما يؤدي إلى نتيجة خاطئة. لحل هذه المشكلة، يجب أن نتأكد من أن عملية الزيادة في count هي ذرية، يمكننا القيام بذلك باستخدام التزامن ولكن جافا 5 java.util.concurrent.atomic توفر فئات تغليف للأعداد الصحيحة والطويلة يمكن استخدامها لتحقيق هذه العملية الذرية دون استخدام التزامن.

مثال 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();
        }
    }

}

من فوائد استخدام فئات التوازي للعمليات الذرية هو أننا لا نحتاج إلى القلق بشأن التزامن. يؤدي هذا إلى تحسين قراءة الشفرة وتقليل فرص الأخطاء. كما يفترض أن تكون فئات التوازي للعمليات الذرية أكثر كفاءة من التزامن الذي ينطوي على قفل الموارد.

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