User Tools

Site Tools


java:thread_memory_model

Το γενικό μοντέλο μνήμης ενός υπολογιστικού συστήματος

Κάθε υπολογιστικό σύστημα συνίσταται κατά βάση από τα εξής:

  1. Μία ή περισσότερες CPUs, με ένα ή περισσότερα cores ανά CPU.
  2. CPU cache (κάθε thread έχει την δική του cache)
  3. Μνήμη RAM.

Το πεδίο obj.count (πεδίο του αντικειμένου obj) που είναι αποθηκευμένο στο heap αρχικά έχει την τιμή 1. Το νήμα A διαβάζει την τιμή του obj.count και την αυξάνει κατά 1, θέτοντας τη σε 2. Η τιμή αποθηκεύεται προσωρινά στην cache του επεξεργαστή, χωρίς να ενημερωθεί η RAM για την αλλαγή. Στη συνέχεια το thread B διαβάζει και αυτό την τιμή της μεταβλητής obj.count από την RAM. Η τιμή που θα διαβάσει είναι 1 (η παλιά τιμή) και όχι 2 (η νέα τιμή). Το νήμα B αυξάνει και αυτό την τιμή κατά 1, θέτοντας την τιμή σε 2.

Από τα παραπάνω παρατηρούμε ότι έχουμε ένα υπολογιστικό λάθος που προκύπτει από την ύπαρξη ενός κρίσιμου τμήματος κώδικα. Το παραπάνω μπορεί να αποφευχθεί εφαρμόζοντας κάποια τεχνική συγχρονισμού, όπως θα δούμε στη συνέχεια.

Το μοντέλο μνήμης της Java

Το μοντέλο μνήμης στην Java δίνεται από το παρακάτω σχήμα. Κάθε νήμα διαθέτει την δική του στοίβα (stack), όπου αποθηκεύονται οι τιμές των τοπικών μεταβλητών. Κάθε νήμα έχει πρόσβαση αποκλειστικά στην δική του στοίβα, χωρίς δυνατότητα πρόσβασης στις στοίβες άλλων νημάτων. Παράλληλα υπάρχει και ο κοινός χώρος αποθήκευσης δεδομένων (το λεγόμενο Heap), όπου αποθηκεύονται όλα τα αντικείμενα που δημιουργούνται κατά την εκτέλεση του προγράμματος.

 Το σχήμα αποτυπώνει το βασικό μοντέλο μνήμης. Διακρίνονται το stack όπου αποθηκεύονται οι τοπικές μεταβλητές των μεθόδων που εκτελούνται από κάθε νήμα και το Heap που αποθηκεύονται τα αντικείμενα που δημιουργούνται από όλα τα νήματα.

Το μοντέλο μνήμης των μεταβλητών

Μεταβλητές ως παράμετροι μεθόδων

Βασικοί τύποι δεδομένων ως παράμετροι μεθόδων

Κάθε τοπική μεταβλητή αποθηκεύεται στη στοίβα (stack). Για τις τοπικές μεταβλητές βασικού τύπου (int, byte, long, double) που γνωρίζουμε εκ των προτέρων το μέγεθος τους αυτές αποθηκεύονται στη στοίβα. Στο παρακάτω παράδειγμα, έχουμε τις μεταβλητές a, b, c. Και οι τρεις μεταβλητές είναι τοπικές δημιουργούνται στην στοίβα του νήματος και καταστρέφονται μετά από την εκτέλεση της μεθόδου add. Κανένα άλλο νήμα δεν μπορεί να έχει πρόσβαση σε τοπικές μεταβλητές βασικού τύπου.

  int add(int a, int b) {
    int sum = a+b;
    return sum;
  }

Αναφορικοί τύποι δεδομένων ως παράμετροι μεθόδων

Όταν δημιουργείται ένα αντικείμενο το οποίο ανατίθεται σε μία τοπική μεταβλητή ενός νήματος, τότε η τοπική μεταβλητή παρέχει μία αναφορά προς το αντικείμενο αυτό. Η τοπική μεταβλητή είναι αποθηκευμένη στο stack, αλλά το αντικείμενο στο οποίο δείχνει η μεταβλητή είναι αποθηκευμένο στο heap. Στο παρακάτω παράδειγμα, οι μεταβλητές a, b, sum, referenceSum είναι τοπικές. Οι a, b, referenceSum όμως δείχνουν σε αντικείμενα τύπου Integer τα οποία είναι αποθηκευμένα στο Heap. Η ζωή των αντικειμένων αυτών εξαρτάται από το κατά πόσο εξακολουθούν να υπάρχουν αναφορές προς αυτά από τοπικές μεταβλητές ή πεδία αντικειμένων ή στατικές μεταβλητές.

Αντικείμενα που είναι αποθηκευμένα στο heap είναι δυνατόν να διαμοιράζονται με άλλα νήματα ανεξάρτητα από την τοπικότητα ή μη των μεταβλητών που δείχνουν σε αυτά.

  Integer add(Integer a, Integer b) {
    int sum = a.intValue() + b.intValue();
    Integer referenceSum = new Integer(sum);
    return referenceSum;
  }

Μεταβλητές ως πεδία της κλάσης που υλοποιεί ένα νήμα

Κάθε κλάση που υλοποιεί ένα νήμα (implements Runnable ή extends Thread) μπορεί να έχει πεδία βασικού ή αναφορικού τύπου. Οι μεταβλητές που είναι πεδία βασικού τύπου αν και αποθηκεύονται στο heap (μαζί με το αντικείμενο) είναι προσβάσιμες μόνο από το συγκεκριμένο νήμα. Εξαίρεση αποτελεί εάν το συγκεκριμένο νήμα παρέχεται ως παράμετρος σε άλλα νήματα.

Αντίθετα, μεταβλητές που είναι αναφορικού τύπου παρά το γεγονός ότι είναι διαφορετικές για κάθε νήμα είναι δυνατόν να δείχνουν σε αντικείμενα που είναι κοινά με άλλα νήματα. Δείτε το παρακάτω παράδειγμα κώδικα, όπου τα δύο νήματα της κλάσης BasicThread διαμοιράζονται ένα αντικείμενο της κλάσης SharedObj.

BasicThread.java
public class BasicThread extends Thread {
 
  SharedObj shared;
  public BasicThread(SharedObj obj) { shared = obj; }
 
  public void run() {
    int localInt = 45;
    Integer localInteger = new Integer(58);
    shared.member++;
  }
 
  public static void main(String []args) {
    SharedObj obj = new SharedObj();
    new BasicThread(obj).start();
    new BasicThread(obj).start();
  }
}
 
class SharedObj {
 
  public Integer object = new Integer(22);
  public long member = 12345;
 
}

Παρακάτω δίνεται η σχηματική αναπαράσταση εκτέλεσης των δύο νημάτων της κλάσης BasicThread.

Στατικές μεταβλητές της κλάσης

Στο παρακάτω κώδικα δύο νήματα της κλάσης MyThread διαμοιράζονται μέσω μίας τοπικής μεταβλητής το ίδιο στατικό πεδίο της κλάσης SharedObj. Καθώς το πεδίο είναι αναφορικού τύπου αποθηκεύεται στο heap και είναι προσβάσιμο και από τα δύο πεδία.

MyThread.java
public class MyThread extends Thread {
 
  public void run() {
    int localInt = 45;
    SharedObj localObj = SharedObj.sharedInstance;
    localObj.member++;
  }
 
  public static void main(String []args) {
    new MyThread().start();
    new MyThread().start();
  }
}
 
class SharedObj {
 
  public static SharedObj sharedInstance = new SharedObj();
  public Integer object = new Integer(22);
  public long member = 12345;
}

Στο παραπάνω διάγραμμα δίνεται σχηματικά η εκτέλεση του παραπάνω κώδικα.

Συμπερασματικά

  1. Μία τοπική μεταβλητή βασικού τύπου (int, long, byte, double etc) αποθηκεύεται στο stack.
  2. Μία τοπική μεταβλητή ενός αναφορικού τόπου αποθηκεύει την αναφορά στο stack, αλλά το αντικείμενο στο heap.
  3. Οποιοδήποτε πεδίο της κλάσης ενός νήματος αποθηκεύεται στο heap μαζί με το αντικείμενο. Όλα τα πεδία βασικού τύπου είναι προσβάσιμα μόνο από το νήμα. Όλα τα πεδία αναφορικού τύπου μπορούν να είναι προσβάσιμα από περισσότερα του ενός νήματα.
  4. Οι στατικές μεταβλητές ανήκουν στις κλάσεις τις οποίες ορίζονται κατά συνέπεια είναι αποθηκευμένες πάντοτε στο heap.
java/thread_memory_model.txt · Last modified: 2018/03/08 09:31 by gthanos