היום נסתכל על 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, 6, 7, 8. הסיבה היא ש-count++ אינה פעולה אטומית. כך שבזמן שתהליך אחד קורא לערך ומעלה אותו באחד, תהליך אחר יכול לקרוא לערך הישן יותר, מה שמוביל לתוצאה שגויה. כדי לפתור את הבעיה הזו, עלינו לוודא שפעולת ההגדלה על ה-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) {
// עיבוד עבודה מסוימת
try {
Thread.sleep(i * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
היתרונות של שימוש במחלקות Concurrency לפעולה אטומית הם שאין צורך לדאוג לסנכרון. זה משפר קריאות של הקוד ומפחית את הסיכוי לשגיאות. גם פעולות קונקורנטיות אטומיות מניחות כי הן יעילות יותר מסנכרון שמשתמש בנעילת משאבים.
Source:
https://www.digitalocean.com/community/tutorials/atomicinteger-java