في جافا، المتغيرات الذرية و عمليات تستخدم في التزامن. ال خيوط متعددة البيئة تؤدي إلى مشكلة عندما التزامن موحد. قد يتغير الكيان المشترك مثل الكائنات والمتغيرات أثناء تنفيذ البرنامج. ومن ثم، فإنها قد تؤدي إلى عدم اتساق البرنامج. لذلك، من المهم الاهتمام بالكيان المشترك أثناء الوصول بشكل متزامن. في مثل هذه الحالات، المتغير الذري يمكن أن يكون حلا لذلك. في هذا القسم سنناقش الطبقات الذرية، المتغيرات الذرية، العمليات الذرية ، مع الأمثلة.
سن ديول المشمس
قبل المضي قدمًا في هذا القسم، تأكد من أنك على علم بذلك خيط , المزامنة ، و قفل في جافا.
جافا الطبقات الذرية
جافا يوفر أ java.util.concurrent.atomic الحزمة التي يتم فيها تعريف الفئات الذرية. توفر الطبقات الذرية أ خالية من القفل و آمن للخيط البيئة أو البرمجة على متغير واحد. كما أنه يدعم العمليات الذرية. جميع الفئات الذرية لديها أساليب get() و set() التي تعمل على المتغير المتطاير. تعمل الطريقة بنفس طريقة القراءة والكتابة على المتغيرات المتقلبة.
توفر الحزمة الفئات الذرية التالية:
فصل | وصف |
---|---|
AtomicBoolean | يتم استخدامه لتحديث القيمة المنطقية ذريًا. |
AtomicInteger | يتم استخدامه لتحديث قيمة عدد صحيح ذريًا. |
AtomicIntegerArray | مصفوفة int يمكن فيها تحديث العناصر ذريًا. |
AtomicIntegerFieldUpdater | أداة مساعدة قائمة على الانعكاس تتيح التحديثات الذرية لحقول int متقلبة محددة للفئات المحددة. |
AtomicLong | يتم استخدامه لتحديث القيمة الطويلة ذريًا. |
AtomicLongArray | مصفوفة طويلة يمكن فيها تحديث العناصر ذريًا. |
AtomicLongFieldUpdater | أداة مساعدة قائمة على الانعكاس تتيح التحديثات الذرية للحقول الطويلة المتقلبة المحددة للفئات المحددة. |
AtomicMarkableReference | يحتفظ AtomicMarkableReference بمرجع الكائن مع بت العلامة، والذي يمكن تحديثه ذريًا. |
مرجع ذري | مرجع كائن يمكن تحديثه ذريًا. |
AtomicReferenceArray | مصفوفة من مراجع الكائنات التي يمكن تحديث العناصر فيها ذريًا. |
AtomicReferenceFieldUpdater | أداة مساعدة قائمة على الانعكاس تتيح إجراء التحديثات الذرية للحقول المرجعية المتطايرة المعينة للفئات المعينة. |
AtomicStampedReference | يحتفظ AtomicStampedReference بمرجع الكائن بالإضافة إلى 'ختم' عدد صحيح، والذي يمكن تحديثه ذريًا. |
تراكم مزدوج | واحد أو أكثر من المتغيرات التي تحافظ معًا على قيمة مزدوجة قيد التشغيل محدثة باستخدام دالة مقدمة. |
DoubleAdder | واحد أو أكثر من المتغيرات التي تحافظ معًا على مجموع مزدوج صفر في البداية. |
تراكم طويل | واحد أو أكثر من المتغيرات التي تحافظ معًا على قيمة طويلة قيد التشغيل محدثة باستخدام دالة مقدمة. |
LongAdder | واحد أو أكثر من المتغيرات التي تحافظ معًا على مجموع طويل صفر في البداية. |
تمثل كائنات هذه الفئات المتغير الذري لـ كثافة العمليات، طويلة، منطقية ، والكائن مرجع على التوالى. الطبقات الذرية لديها بعض الطرق الشائعة هي كما يلي:
طُرق | وصف |
---|---|
تعيين() | يتم استخدامه لتعيين القيمة. |
يحصل() | يتم استخدامه للحصول على القيمة الحالية. |
كسول سيت () | يتم تعيينه في النهاية إلى القيمة المحددة. |
قارن وSet | يضبط القيمة تلقائيًا على القيمة المحدثة المحددة إذا كانت القيمة الحالية == القيمة المتوقعة. |
العمليات الذرية
تُعرف تلك العمليات التي يتم تنفيذها دائمًا معًا باسم العمليات الذرية أو العمل الذري . جميع العمليات الذرية إما أن يتم تنفيذها بفعالية، أو أنها لا تحدث على الإطلاق. ثلاثة المفاهيم الأساسية المرتبطة بالإجراءات الذرية في 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 على 'صحيح'. أيضًا، لا يعني ذلك عدم وجود تزامن بين سلاسل الرسائل.
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. إذا قمنا باستدعاء الطريقة أعلاه من خلال خيطين منفصلين، فسيصل كلاهما إلى المتغير في نفس الوقت ويقومان أيضًا بتحديث قيمة العد في وقت واحد. لتجنب هذا الوضع، نستخدم العملية الذرية.
تدعم Java عدة أنواع من الإجراءات الذرية، وهي كما يلي:
- متقلب المتغيرات
- العمليات الذرية ذات المستوى المنخفض (غير الآمنة)
- الطبقات الذرية
دعونا نرى كيف يمكننا إنشاء عملية ذرية.
المتغير الذري
يسمح لنا المتغير الذري بإجراء عملية ذرية على متغير. تعمل المتغيرات الذرية على تقليل المزامنة وتساعد على تجنب أخطاء تناسق الذاكرة. وبالتالي، فإنه يضمن التزامن.
توفر الحزمة الذرية المتغيرات الذرية الخمسة التالية:
- AtomicInteger
- AtomicLong
- AtomicBoolean
- 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 واستخدام متغير ذري للتغلب على المشكلة.
باستخدام المتغير الذري
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;>
متزامنة مقابل. الذرية مقابل. متقلب
متزامن | الذري | متقلب |
---|---|---|
ينطبق على الأساليب فقط. | ينطبق على المتغيرات فقط | كما أنه ينطبق على المتغيرات فقط. |
فهو يضمن الرؤية جنبا إلى جنب مع الذرية. | كما أنه يضمن الرؤية جنبا إلى جنب مع الذرية. | إنه يضمن الرؤية وليس الذرية. |
لا يمكننا تحقيق نفس الشيء. | لا يمكننا تحقيق نفس الشيء. | يتم تخزينه في ذاكرة الوصول العشوائي (RAM)، لذا فإن الوصول إلى المتغيرات المتقلبة يكون سريعًا. لكنها لا توفر سلامة الخيط والتزامن. |
يمكن تنفيذه ككتلة متزامنة أو طريقة متزامنة. | لا يمكننا تحقيق نفس الشيء. | لا يمكننا تحقيق نفس الشيء. |
يمكنه قفل نفس كائن الفئة أو كائن فئة مختلفة. | لا يمكننا تحقيق نفس الشيء. | لا يمكننا تحقيق نفس الشيء. |