ב-Java, משתנים אטומיים ו פעולות בשימוש במקביל. ה ריבוי השחלות הסביבה מובילה לבעיה כאשר במקביל מאוחדת. הישות המשותפת כגון אובייקטים ומשתנים עשויה להשתנות במהלך ביצוע התוכנית. לפיכך, הם עלולים להוביל לחוסר עקביות של התוכנית. לכן, חשוב לטפל בישות המשותפת תוך גישה במקביל. במקרים כאלה, ה משתנה אטומי יכול להיות פתרון לזה. בחלק זה, נדון מחלקות אטומיות, משתנים אטומיים, פעולות אטומיות , יחד עם דוגמאות.
יצוק ב-sql
לפני שתמשיך בסעיף זה, ודא שאתה מודע לכך פְּתִיל , סִנכְּרוּן , ו לנעול ב-Java.
שיעורי ג'אווה אטומיים
Java מספק java.util.concurrent.atomic חבילה שבה מוגדרות מחלקות אטומיות. הכיתות האטומיות מספקות א ללא נעילה ו בטוח בחוט סביבה או תכנות על משתנה בודד. זה גם תומך בפעולות אטומיות. לכל המחלקות האטומיות יש את המתודות get() ו-set() שעובדות על המשתנה הנדיף. השיטה פועלת כמו קריאה וכתיבה על משתנים נדיפים.
החבילה מספקת את השיעורים האטומיים הבאים:
מעמד | תיאור |
---|---|
Atomic Boolean | הוא משמש לעדכון ערך בוליאני מבחינה אטומית. |
AtomicInteger | הוא משמש לעדכון ערך מספר שלם באופן אטומי. |
AtomicIntegerArray | מערך int שבו אלמנטים עשויים להתעדכן מבחינה אטומית. |
AtomicIntegerFieldUpdater | כלי עזר מבוסס השתקפות המאפשר עדכונים אטומיים לשדות אינט נדיפים ייעודיים של מחלקות ייעודיות. |
AtomicLong | הוא משמש לעדכון ערך ארוך מבחינה אטומית. |
AtomicLongArray | מערך ארוך שבו אלמנטים עשויים להתעדכן מבחינה אטומית. |
AtomicLongFieldUpdater | כלי עזר מבוסס השתקפות המאפשר עדכונים אטומיים לשדות ארוכים נדיפים ייעודיים של מחלקות ייעודיות. |
AtomicMarkableReference | AtomicMarkableReference שומר על הפניה לאובייקט יחד עם סיביות סימן, שניתן לעדכן בצורה אטומית. |
AtomicReference | הפניה לאובייקט שעשויה להתעדכן מבחינה אטומית. |
AtomicReferenceArray | מערך של הפניות לאובייקט שבו אלמנטים עשויים להתעדכן מבחינה אטומית. |
AtomicReferenceFieldUpdater | כלי עזר מבוסס השתקפות המאפשר עדכונים אטומיים לשדות ייחוס נדיפים ייעודיים של מחלקות ייעודיות. |
AtomicStampedReference | AtomicStampedReference שומר על הפניה לאובייקט יחד עם 'חותמת' של מספר שלם, שניתן לעדכן בצורה אטומית. |
מצבר כפול | משתנה אחד או יותר שיחד שומרים על ערך כפול פועל המעודכן באמצעות פונקציה שסופקה. |
DoubleAdder | משתנה אחד או יותר שיחד שומרים על סכום כפול אפס בהתחלה. |
מצבר ארוך | משתנה אחד או יותר שיחד שומרים על ערך רץ ארוך המעודכן באמצעות פונקציה שסופקה. |
LongAdder | משתנה אחד או יותר שיחד שומרים על סכום ארוך בהתחלה אפס. |
אובייקטים של מחלקות אלה מייצגים את המשתנה האטומי של int, ארוך, בוליאני , ומתנגד התייחסות בהתאמה. למחלקות האטומיות יש כמה שיטות נפוצות הן כדלקמן:
שיטות | תיאור |
---|---|
מַעֲרֶכֶת() | הוא משמש להגדרת הערך. |
לקבל() | הוא משמש כדי לקבל את הערך הנוכחי. |
lazySet() | בסופו של דבר קובע לערך הנתון. |
compareAndSet | אטומי מגדיר את הערך לערך המעודכן הנתון אם הערך הנוכחי == הערך הצפוי. |
פעולות אטומיות
הפעולות האלה שתמיד מבוצעות ביחד ידועות בשם פעולות אטומיות אוֹ פעולה אטומית . כל הפעולות האטומיות המבוצעות ביעילות מתרחשות בבת אחת או שאינן מתרחשות כלל. שְׁלוֹשָׁה מושגי מפתח הקשורים לפעולות אטומיות ב-Java הם כדלקמן:
1. האטומיות עוסקת באילו פעולות וקבוצות יש לפעולות בלתי נראה לדוגמה, שקול את קטע הקוד הבא:
class NoAtomicOps { long counter=0; void increment() { for(;;) { count++; } } void decrement() { for(;;) { count--; } } //other statement }
בקוד שלמעלה, ההתנהגות של הפעלת increment() ו-decrement() במקביל היא לא מוגדר ו לא צפוי .
2. הנראות קובעת מתי יכולה להיות ההשפעה של חוט אחד ראה על ידי אחר. לדוגמה, שקול את קטע הקוד הבא:
class InfiniteLoop { boolean done= false; void work() { //thread T2 read while(!done) { //do work } } void stopWork() { //thread T1 write done=true; } //statements }
בקוד שלמעלה, ייתכן שהשרשור T2 לעולם לא ייעצר גם לאחר שהשרשור T1 מוגדר כ-true. כמו כן, לא שאין סנכרון בין שרשורים.
3. סדר קובע מתי פעולות בשרשור אחד מתרחשות לא בסדר ביחס לשרשור אחר.
class Order { boolean a=false; boolean b=false; void demo1() //thread T1 { a=true; b=true; } boolean demo2() //thread T2 { boolean r1=b; //sees true boolean r2=a; //sees false boolean r3=a; //sees true //returns true return (r1 && !r2) && r3; } }
הסדר שבו מופיעים שדות a ו-b בשרשור T2 עשוי להיות שונה מהסדר שהם נקבעו בשרשור T1.
בונה מחרוזות
בואו נבין את זה באמצעות דוגמה.
public class AtomicExample { int count; public void incrementCount() { count=1; }
בקטע הקוד שלמעלה, הכרזנו על משתנה מסוג int לספור ובתוך המתודה incrementCount() הקצתה אותו ל-1. במקרה כזה, או שכולם קורים ביחד או שלא יקרו בכלל. לפיכך, הוא מייצג א פעולה אטומית והמבצע ידוע בשם אָטוֹמִיוּת .
הבה נשקול קטע קוד נוסף.
public class AtomicExample { int count; public void incrementCount() { count=count+1; }
נראה שגם מדובר בפעולה אטומית אבל לא כך. זוהי פעולה ליניארית המורכבת משלוש פעולות כלומר קריאה, שינוי וכתיבה. לכן, זה יכול לבצע חלקית. אבל אם אנחנו משתמשים בקוד לעיל בסביבה מרובת הליכי הליכה, זה יוצר בעיה.
נניח שקראנו לקוד הנ'ל בסביבה עם חוט יחיד, הערך המעודכן של ספירה יהיה 2. אם נקרא לשיטה שלעיל על ידי שני שרשורים נפרדים, הם גם ניגשים למשתנה בו זמנית וגם מעדכנים את הערך של לספור בו זמנית. כדי להימנע ממצב זה, אנו משתמשים בפעולה אטומית.
JavaScript לדוגמא
Java תומכת במספר סוגים של פעולות אטומיות, הן כדלקמן:
- נָדִיף משתנים
- פעולות אטומיות ברמה נמוכה (לא בטוחות)
- שיעורים אטומיים
בואו נראה איך נוכל ליצור פעולה אטומית.
משתנה אטומי
המשתנה האטומי מאפשר לנו לבצע פעולה אטומית על משתנה. משתנים אטומיים ממזערים את הסנכרון ועוזרים להימנע משגיאות עקביות בזיכרון. לפיכך, זה מבטיח סנכרון.
מסגרת אוסף java
החבילה האטומית מספקת את חמשת המשתנים האטומיים הבאים:
- AtomicInteger
- AtomicLong
- Atomic Boolean
- AtomicIntegerArray
- AtomicLongArray
הצורך במשתנה אטומי
הבה נשקול את הקוד הבא.
Counter.java
class Counter extends Thread { // Counter Variable int count = 0; //the method starts the execution of a thread public void run() { int max = 1; //increments the counter variable up to specified max time for (int i = 0; i <max; i++) { count++; } public class counter static void main(string args[]) throws interruptedexception creating an instance of the c="new" counter(); four threads thread t1="new" thread(c, 'first'); t2="new" 'second'); t3="new" 'third'); t4="new" 'fourth'); by calling start() method, we have started t1.start(); t2.start(); t3.start(); t4.start(); main will wait for all until execution do not complete t1.join(); t2.join(); t3.join(); t4.join(); prints final value count variable system.out.println(c.count); < pre> <p> <strong>Output:</strong> </p> <pre> 4 </pre> <p>The above program gives the expected output if it is executed in a single-threaded environment. A multi-threaded environment may lead to unexpected output. The reason behind it that when two or more threads try to update the value at the same time then it may not update properly.</p> <p>Java offers <strong>two</strong> solutions to overcome this problem:</p> <ul> <li>By using lock and synchronization</li> <li>By using atomic variable</li> </ul> <p>Let's create a Java program and use an atomic variable to overcome the problem.</p> <h3>By using Atomic Variable</h3> <p> <strong>AtomicExample.java</strong> </p> <pre> class Counter extends Thread { // Counter Variable int count = 0; //the method starts the execution of a thread public void run() { int max = 1; //increments the counter variable up to specified max time for (int i = 0; i <max; i++) { count++; } public class counter static void main(string args[]) throws interruptedexception creating an instance of the c="new" counter(); four threads thread t1="new" thread(c, 'first'); t2="new" 'second'); t3="new" 'third'); t4="new" 'fourth'); by calling start() method, we have started t1.start(); t2.start(); t3.start(); t4.start(); main will wait for all until execution do not complete t1.join(); t2.join(); t3.join(); t4.join(); prints final value count variable system.out.println(c.count); < pre> <p> <strong>Output:</strong> </p> <pre> 4 </pre> <h2>Synchronized Vs. Atomic Vs. Volatile</h2> <table class="table"> <tr> <th>Synchronized</th> <th>Atomic</th> <th>Volatile</th> </tr> <tr> <td>It applies to methods only.</td> <td>It applies to variables only.</td> <td>It also applies to variables only.</td> </tr> <tr> <td>It ensures visibility along with atomicity.</td> <td>It also ensures visibility along with atomicity.</td> <td>It ensures visibility, not atomicity.</td> </tr> <tr> <td>We can't achieve the same.</td> <td>We can't achieve the same.</td> <td>It stores in RAM, so accessing volatile variables is fast. But it does not provide thread-safety and synchronization.</td> </tr> <tr> <td>It can be implemented as a synchronized block or a synchronized method.</td> <td>We can't achieve the same.</td> <td>We can't achieve the same.</td> </tr> <tr> <td>It can lock the same class object or a different class object.</td> <td>We can't achieve the same.</td> <td>We can't achieve the same.</td> </tr> </table> <hr></max;></pre></max;>
התוכנית לעיל נותנת את הפלט הצפוי אם היא מבוצעת בסביבה עם חוט יחיד. סביבה מרובת חוטים עלולה להוביל לפלט בלתי צפוי. הסיבה מאחורי זה שכאשר שני שרשורים או יותר מנסים לעדכן את הערך בו זמנית אז הוא עלול לא להתעדכן כראוי.
Java מציעה שתיים פתרונות להתגבר על בעיה זו:
- באמצעות נעילה וסנכרון
- על ידי שימוש במשתנה אטומי
בואו ניצור תוכנית Java ונשתמש במשתנה אטומי כדי להתגבר על הבעיה.
על ידי שימוש במשתנה אטומי
AtomicExample.java
class Counter extends Thread { // Counter Variable int count = 0; //the method starts the execution of a thread public void run() { int max = 1; //increments the counter variable up to specified max time for (int i = 0; i <max; i++) { count++; } public class counter static void main(string args[]) throws interruptedexception creating an instance of the c="new" counter(); four threads thread t1="new" thread(c, \'first\'); t2="new" \'second\'); t3="new" \'third\'); t4="new" \'fourth\'); by calling start() method, we have started t1.start(); t2.start(); t3.start(); t4.start(); main will wait for all until execution do not complete t1.join(); t2.join(); t3.join(); t4.join(); prints final value count variable system.out.println(c.count); < pre> <p> <strong>Output:</strong> </p> <pre> 4 </pre> <h2>Synchronized Vs. Atomic Vs. Volatile</h2> <table class="table"> <tr> <th>Synchronized</th> <th>Atomic</th> <th>Volatile</th> </tr> <tr> <td>It applies to methods only.</td> <td>It applies to variables only.</td> <td>It also applies to variables only.</td> </tr> <tr> <td>It ensures visibility along with atomicity.</td> <td>It also ensures visibility along with atomicity.</td> <td>It ensures visibility, not atomicity.</td> </tr> <tr> <td>We can't achieve the same.</td> <td>We can't achieve the same.</td> <td>It stores in RAM, so accessing volatile variables is fast. But it does not provide thread-safety and synchronization.</td> </tr> <tr> <td>It can be implemented as a synchronized block or a synchronized method.</td> <td>We can't achieve the same.</td> <td>We can't achieve the same.</td> </tr> <tr> <td>It can lock the same class object or a different class object.</td> <td>We can't achieve the same.</td> <td>We can't achieve the same.</td> </tr> </table> <hr></max;>
מסונכרן לעומת Atomic Vs. נָדִיף
מסונכרן | אָטוֹמִי | נָדִיף |
---|---|---|
זה חל על שיטות בלבד. | זה חל על משתנים בלבד. | זה חל גם על משתנים בלבד. |
זה מבטיח נראות יחד עם אטומיות. | זה גם מבטיח נראות יחד עם אטומיות. | זה מבטיח נראות, לא אטומיות. |
אנחנו לא יכולים להשיג את אותו הדבר. | אנחנו לא יכולים להשיג את אותו הדבר. | הוא מאחסן ב-RAM, כך שהגישה למשתנים נדיפים היא מהירה. אבל זה לא מספק בטיחות חוט וסנכרון. |
זה יכול להיות מיושם כבלוק מסונכרן או שיטה מסונכרנת. | אנחנו לא יכולים להשיג את אותו הדבר. | אנחנו לא יכולים להשיג את אותו הדבר. |
זה יכול לנעול את אותו אובייקט מחלקה או אובייקט מחלקה אחר. | אנחנו לא יכולים להשיג את אותו הדבר. | אנחנו לא יכולים להשיג את אותו הדבר. |