Τα νήματα κατά κανόνα επικοινωνούν μέσω μεταβλητών στις οποίες μοιράζονται την πρόσβαση. Το πρόβλημα με αυτού του είδους την επικοινωνία είναι ότι είναι πιθανό να παρουσιαστεί ασάφεια ως προς τις τιμές των μεταβλητών που γράφονται οι διαβάζονται από τα παράλληλα νήματα. Το πρόβλημα της ασάφειας εξηγείται παρακάτω μέσα από ένα παράδειγμα.
Στο παρακάτω παράδειγμα κώδικα ας υποθέσουμε ότι ένα αντικείμενο της κλάσης Counter διαμοιράζεται μεταξύ δύο νημάτων. Η ασάφεια ως προς τις τιμές μίας μεταβλητής παρουσιάζεται όταν δύο νήματα προσπαθούν ταυτόχρονα να μεταβάλλουν την τιμή της μεταβλητής.
class Counter { private int c = 0; public void increment() { c++; } public void decrement() { c--; } public int value() { return c; } }
Ας υποθέσουμε ότι έχουμε δύο νήματα Α,Β και ότι η μεταβλητή c έχει αρχικά την τιμή 0. Τα δύο νήματα επιχειρούν να μεταβάλλουν την τιμή της c ταυτόχρονα. Η μεταβολή της τιμής της μεταβλητής c γίνεται σε τρία βήματα
Με βάση τα παραπάνω ας υποθέσουμε την παρακάτω ακολουθία
Μετά από το παραπάνω σενάριο η τιμή της c θα έπρεπε να παραμένει και πάλι μηδέν καθώς το ένα νήμα την αύξησε και το άλλο την μείωσε. Αντί για αυτό η τιμή της c είναι -1 καθώς η μεταβολή από το νήμα Α στην πράξη δεν έγινε. Για να μεταβληθεί σωστά η τιμή της c η παραπάνω ακολουθία θα έπρεπε να είναι η εξής
Ο παρακάτω κώδικας υλοποιεί το παραπάνω σενάριο. Δύο νήματα μεταβάλλουν την τιμή ενός μετρητή για SIZE φορές (το ένα νήμα αυξάνει την τιμή κατά ένα και το άλλο μειώνει την τιμή κατά ένα). Μεταγλωττίστε και εκτελέστε τον παρακάτω κώδικα για διάφορες τιμές του SIZE. Τι παρατηρείτε όταν η τιμή του SIZΕ αυξάνει;
public class ModifyCounter extends Thread { Counter counter; boolean increment; static final int SIZE = 10000; ModifyCounter(Counter c, boolean incr, String name) { super(name); counter = c; increment = incr; } public void run() { for(int i=0; i<SIZE; i++) if(increment) counter.increment(); else counter.decrement(); System.out.println(getName() + ". Counter is: "+counter.value()); } public static void main(String []args) { Counter c = new Counter(); new ModifyCounter(c, true, "IncreaseThread").start(); new ModifyCounter(c, false, "DecreaseThread").start(); } }
Προκειμένου να αντιμετωπιστεί η ασάφεια στις τιμές των μεταβλητών κάθε κρίσιμο τμήμα κώδικα (δηλαδή το τμήμα του κώδικα που περιέχει πιθανή ασάφεια) θα πρέπει να εκτελείται κάθε φορά μόνο από ένα νήμα. Προκειμένου να εκτελείται μόνο ένα νήμα σε συγκεκριμένα τμήματα κώδικα θα χρειαστείτε την εισαγωγή συγχρονισμένων μεθόδων ή blocks την οποία θα δούμε στη συνέχεια.
Προηγούμενο: Ταυτόχρονος προγραμματισμός με χρήση νημάτων | Περιεχόμενα | Επόμενο: Το μοντέλο μνήμης της Java |