window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster υπάρχει ήδη') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Ταυτόχρονη χρήση της Java Μέρος 1 - Εισαγωγή - The Codest
The Codest
  • Σχετικά με εμάς
  • Υπηρεσίες
    • Ανάπτυξη λογισμικού
      • Ανάπτυξη Frontend
      • Backend Ανάπτυξη
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Μηχανικοί δεδομένων
      • Μηχανικοί cloud
      • Μηχανικοί QA
      • Άλλα
    • Συμβουλευτική
      • Έλεγχος & Συμβουλευτική
  • Βιομηχανίες
    • Fintech & Τραπεζική
    • E-commerce
    • Adtech
    • Healthtech
    • Κατασκευή
    • Εφοδιαστική
    • Αυτοκίνητο
    • IOT
  • Αξία για
    • CEO
    • CTO
    • Διευθυντής παράδοσης
  • Η ομάδα μας
  • Case Studies
  • Μάθετε πώς
    • Blog
    • Συναντήσεις
    • Διαδικτυακά σεμινάρια
    • Πόροι
Καριέρα Ελάτε σε επαφή
  • Σχετικά με εμάς
  • Υπηρεσίες
    • Ανάπτυξη λογισμικού
      • Ανάπτυξη Frontend
      • Backend Ανάπτυξη
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Μηχανικοί δεδομένων
      • Μηχανικοί cloud
      • Μηχανικοί QA
      • Άλλα
    • Συμβουλευτική
      • Έλεγχος & Συμβουλευτική
  • Αξία για
    • CEO
    • CTO
    • Διευθυντής παράδοσης
  • Η ομάδα μας
  • Case Studies
  • Μάθετε πώς
    • Blog
    • Συναντήσεις
    • Διαδικτυακά σεμινάρια
    • Πόροι
Καριέρα Ελάτε σε επαφή
Πίσω βέλος GO BACK
2022-06-15
Ανάπτυξη λογισμικού

Ταυτόχρονη χρήση της Java Μέρος 1 - Εισαγωγή

The Codest

Rafal Sawicki

Προγραμματιστής Java

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

Γενικά, η συμβατική προσέγγιση προγραμματισμού είναι διαδοχική. Τα πάντα σε ένα πρόγραμμα συμβαίνουν ένα βήμα τη φορά.
Αλλά, στην πραγματικότητα, η παράλληλη λειτουργία είναι ο τρόπος με τον οποίο λειτουργεί όλος ο κόσμος - είναι η ικανότητα να εκτελούνται ταυτόχρονα περισσότερες από μία εργασίες.

Νήμα vs. διαδικασία

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

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

Το νήμα είναι ένα είδος διεργασίας, αλλά σε χαμηλότερο επίπεδο, γι' αυτό και είναι επίσης γνωστό ως ελαφρύ νήμα. Πολλαπλά νήματα μπορούν να εκτελούνται σε ένα
διαδικασία. Εδώ το πρόγραμμα ενεργεί ως χρονοπρογραμματιστής και ελεγκτής για τα νήματα. Με αυτόν τον τρόπο τα μεμονωμένα προγράμματα φαίνεται να κάνουν
πολλαπλές εργασίες ταυτόχρονα.

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

Διαδικασίες, νήματα - εντάξει... Αλλά τι ακριβώς είναι η ταυτόχρονη λειτουργία; Ταυτόχρονη εκτέλεση σημαίνει ότι μπορείτε να εκτελείτε πολλαπλές εργασίες ταυτόχρονα.
ώρα. Αυτό δεν σημαίνει ότι οι εργασίες αυτές πρέπει να εκτελούνται ταυτόχρονα - αυτό είναι ο παραλληλισμός. Concurrenc στη Javay επίσης δεν
απαιτούν να έχετε πολλαπλές CPU ή ακόμη και πολλαπλούς πυρήνες. Μπορεί να επιτευχθεί σε περιβάλλον ενός πυρήνα με την αξιοποίηση
εναλλαγή περιβάλλοντος.

Ένας όρος που σχετίζεται με την ταυτόχρονη εκτέλεση είναι το multithreading. Πρόκειται για ένα χαρακτηριστικό των προγραμμάτων που τους επιτρέπει να εκτελούν πολλές εργασίες ταυτόχρονα. Δεν χρησιμοποιούν όλα τα προγράμματα αυτή την προσέγγιση, αλλά αυτά που το κάνουν μπορούν να αποκαλούνται πολυνηματικά.

Είμαστε σχεδόν έτοιμοι να φύγουμε, μόνο ένας ορισμός ακόμα. Ασυνγχρονισμός σημαίνει ότι ένα πρόγραμμα εκτελεί λειτουργίες που δεν μπλοκάρουν.
Εκκινεί μια εργασία και στη συνέχεια συνεχίζει με άλλα πράγματα περιμένοντας την απάντηση. Όταν λάβει την απάντηση, μπορεί να αντιδράσει σε αυτήν.

Όλη αυτή η τζαζ

Από προεπιλογή, κάθε Εφαρμογή Java τρέχει σε μία διαδικασία. Σε αυτή τη διεργασία, υπάρχει ένα νήμα που σχετίζεται με το main() μέθοδος της
μια εφαρμογή. Ωστόσο, όπως αναφέρθηκε, είναι δυνατή η αξιοποίηση των μηχανισμών πολλαπλών νημάτων μέσα σε ένα
πρόγραμμα.

Εκτελέσιμο

Νήμα είναι μια Java κλάση στην οποία συμβαίνει η μαγεία. Αυτή είναι η αντικειμενική αναπαράσταση του προαναφερθέντος νήματος. Στο
να δημιουργήσετε το δικό σας νήμα, μπορείτε να επεκτείνετε την Νήμα κατηγορία. Ωστόσο, δεν συνιστάται αυτή η προσέγγιση. Νήματα θα πρέπει να χρησιμοποιηθεί ως μηχανισμός εκτέλεσης της εργασίας. Οι εργασίες είναι κομμάτια κωδικός που θέλουμε να τρέξουμε σε ταυτόχρονη λειτουργία. Μπορούμε να τα ορίσουμε χρησιμοποιώντας την εντολή Εκτελέσιμο διεπαφή.

Αρκετά όμως με τη θεωρία, ας βάλουμε τον κώδικά μας εκεί που είναι το στόμα μας.

Πρόβλημα

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

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7},

Runnable task1 = () -> {
    int sum = Arrays.stream(a1).sum(),
    System.out.println("1. Το άθροισμα είναι: " + sum),
};

Runnable task2 = () -> {
    int sum = Arrays.stream(a2).sum(),
    System.out.println("2. Το άθροισμα είναι: " + sum),
};

Runnable task3 = () -> {
    int sum = Arrays.stream(a3).sum(),
    System.out.println("3. Το άθροισμα είναι: " + sum),
};

new Thread(task1).start(),
new Thread(task2).start(),
new Thread(task3).start(),

Όπως μπορείτε να δείτε από τον παραπάνω κώδικα Εκτελέσιμο είναι μια λειτουργική διεπαφή. Περιέχει μία μόνο αφηρημένη μέθοδο run()
χωρίς επιχειρήματα. Το Εκτελέσιμο διεπαφή θα πρέπει να υλοποιείται από κάθε κλάση της οποίας οι περιπτώσεις προορίζονται να είναι
εκτελείται από ένα νήμα.

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

Το τελικό βήμα είναι να start() ένα πρόσφατα δημιουργημένο νήμα. Στο API υπάρχουν επίσης run() μέθοδοι στο Εκτελέσιμο και σε
Νήμα. Ωστόσο, αυτός δεν είναι ένας τρόπος για να αξιοποιήσετε την ταυτόχρονη χρήση της Java. Μια άμεση κλήση σε κάθε μία από αυτές τις μεθόδους οδηγεί σε
εκτελώντας την εργασία στο ίδιο νήμα το main() η μέθοδος εκτελείται.

Συσσώρευση νημάτων και εκτελεστές

Όταν υπάρχουν πολλές εργασίες, η δημιουργία ξεχωριστού νήματος για κάθε μία δεν είναι καλή ιδέα. Η δημιουργία ενός Νήμα είναι μια
βαριά λειτουργία και είναι πολύ καλύτερο να επαναχρησιμοποιείτε τα υπάρχοντα νήματα παρά να δημιουργείτε νέα.

Όταν ένα πρόγραμμα δημιουργεί πολλά νήματα μικρής διάρκειας, είναι προτιμότερο να χρησιμοποιείτε μια δεξαμενή νημάτων. Η δεξαμενή νημάτων περιέχει έναν αριθμό
έτοιμα προς εκτέλεση αλλά μη ενεργά νήματα. Δίνοντας ένα Εκτελέσιμο στη δεξαμενή προκαλεί ένα από τα νήματα να καλέσει την εντολή
run() μέθοδος της δεδομένης Εκτελέσιμο. Μετά την ολοκλήρωση μιας εργασίας το νήμα εξακολουθεί να υπάρχει και να βρίσκεται σε κατάσταση αδράνειας.

Εντάξει, το καταλάβατε - προτιμήστε τη συγκέντρωση νημάτων αντί για χειροκίνητη δημιουργία. Αλλά πώς μπορείτε να χρησιμοποιήσετε τις δεξαμενές νημάτων; Το Εκτελεστές
έχει έναν αριθμό στατικών μεθόδων εργοστασίου για την κατασκευή δεξαμενών νημάτων. Για παράδειγμα newCachedThredPool() δημιουργεί το
μια δεξαμενή στην οποία δημιουργούνται νέα νήματα ανάλογα με τις ανάγκες και τα αδρανή νήματα διατηρούνται για 60 δευτερόλεπτα. Αντίθετα,
newFixedThreadPool() περιέχει ένα σταθερό σύνολο νημάτων, στο οποίο τα αδρανή νήματα διατηρούνται επ' αόριστον.

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

ExecutorService executor = Executors.newCachedThreadPool(),

executor.submit(task1),
executor.submit(task2),
executor.submit(task3),

executor.shutdown(),

Ανακλητό

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

Ανακλητό είναι παρόμοια με Εκτελέσιμο κατά κάποιο τρόπο περιτυλίγει επίσης ασύγχρονες εργασίες. Η κύρια διαφορά είναι ότι είναι σε θέση να
επιστρέφουν μια τιμή. Η τιμή επιστροφής μπορεί να είναι οποιουδήποτε (μη πρωταρχικού) τύπου όπως η Ανακλητό είναι ένας παραμετροποιημένος τύπος.
Ανακλητό είναι μια λειτουργική διεπαφή που έχει call() μέθοδος η οποία μπορεί να πετάξει ένα Εξαίρεση.

Τώρα ας δούμε πώς μπορούμε να αξιοποιήσουμε Ανακλητό στο πρόβλημα της συστοιχίας μας.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7},

Callable task1 = () -> Arrays.stream(a1).sum(),
Callable task2 = () -> Arrays.stream(a2).sum(),
Callable task3 = () -> Arrays.stream(a3).sum(),

ExecutorService executor = Executors.newCachedThreadPool(),
Future future1 = executor.submit(task1),
Future future2 = executor.submit(task2),
Future future3 = executor.submit(task3),

System.out.println("1. Το άθροισμα είναι: " + future1.get()),
System.out.println("2. Το άθροισμα είναι: " + future2.get()),
System.out.println("3. Το άθροισμα είναι: " + future3.get()),

executor.shutdown(),

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

Για να ανακτήσετε το αποτέλεσμα από Μελλοντικό πρέπει να καλέσουμε get() μέθοδος. Εδώ μπορούν να συμβούν δύο πράγματα. Πρώτον, η
το αποτέλεσμα του υπολογισμού που εκτελείται από Ανακλητό είναι διαθέσιμη. Τότε το παίρνουμε αμέσως. Δεύτερον, το αποτέλεσμα δεν είναι
έτοιμο ακόμα. Σε αυτή την περίπτωση get() θα μπλοκάρει μέχρι να είναι διαθέσιμο το αποτέλεσμα.

ComputableFuture

Το θέμα με Μελλοντικό είναι ότι λειτουργεί με το παράδειγμα της "ώθησης". Όταν χρησιμοποιείται Μελλοντικό πρέπει να είσαι σαν ένα αφεντικό που
ρωτάει συνεχώς: "Τελείωσε η εργασία σας; Είναι έτοιμο;' μέχρι να δώσει ένα αποτέλεσμα. Η δράση υπό συνεχή πίεση είναι
ακριβά. Πολύ καλύτερη προσέγγιση θα ήταν να παραγγείλετε Μελλοντικό τι να κάνει όταν είναι έτοιμο με το έργο του. Δυστυχώς,
Μελλοντικό δεν μπορεί να το κάνει αυτό αλλά ComputableFuture μπορεί.

ComputableFuture λειτουργεί με το "παράδειγμα έλξης". Μπορούμε να του πούμε τι να κάνει με το αποτέλεσμα όταν ολοκληρώσει τις εργασίες του. Είναι
είναι ένα παράδειγμα ασύγχρονης προσέγγισης.

ComputableFuture λειτουργεί άψογα με Εκτελέσιμο αλλά όχι με Ανακλητό. Αντ' αυτού, είναι δυνατόν να παρέχετε μια εργασία στο
ComputableFuture με τη μορφή Προμηθευτής.

Ας δούμε πώς σχετίζονται τα παραπάνω με το πρόβλημά μας.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7},

CompletableFuture.supplyAsync(() -> Arrays.stream(a1).sum())
                .thenAccept(System.out::println),

CompletableFuture.supplyAsync(() -> Arrays.stream(a2).sum())
                .thenAccept(System.out::println),

CompletableFuture.supplyAsync(() -> Arrays.stream(a3).sum())
                .thenAccept(System.out::println),

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

Εργασία προς ΟλοκληρώσιμοΜέλλον μπορεί να παρασχεθεί από supplyAsync() μέθοδος που παίρνει Προμηθευτής ή με runAsync() που
παίρνει Εκτελέσιμο. Ένα callback - ένα κομμάτι κώδικα που θα πρέπει να εκτελεστεί με την ολοκλήρωση της εργασίας - ορίζεται από thenAccept()
μέθοδος.

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

Java παρέχει πολλές διαφορετικές προσεγγίσεις για την ταυτόχρονη χρήση. Σε αυτό το άρθρο, μόλις που αγγίξαμε το θέμα.

Παρ' όλα αυτά, καλύψαμε τα βασικά Νήμα, Εκτελέσιμο, Ανακλητό, και CallableFuture το οποίο θέτει ένα καλό σημείο
για περαιτέρω διερεύνηση του θέματος.

Σχετικά άρθρα

Ανάπτυξη λογισμικού

9 λάθη που πρέπει να αποφύγετε κατά τον προγραμματισμό σε Java

Ποια λάθη πρέπει να αποφεύγονται κατά τον προγραμματισμό σε Java; Στο παρακάτω κομμάτι απαντάμε σε αυτό το ερώτημα.

The Codest
Rafal Sawicki Προγραμματιστής Java
Λύσεις Enterprise & Scaleups

Ο σωστός τρόπος για να βρείτε κορυφαίους προγραμματιστές Java

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

The Codest
Grzegorz Rozmus Επικεφαλής μονάδας Java
Λύσεις Enterprise & Scaleups

10 εταιρείες του Ντουμπάι που αξίζει να παρακολουθήσετε το 2020

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

Τόνος Pinar
Λύσεις Enterprise & Scaleups

Πώς η Java μπορεί να υποστηρίξει την επιχείρησή σας;

Πριν ξεκινήσουμε, θα ήθελα να σας υπενθυμίσω ένα σημαντικό πράγμα. Η Java δεν είναι μόνο μια γλώσσα προγραμματισμού.

Bartlomiej Kuczynski
Λύσεις Enterprise & Scaleups

Μια μέρα στη ζωή ενός προγραμματιστή στο The Codest

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

The Codest
Pawel Rybczynski Software Engineer

Εγγραφείτε στη βάση γνώσεών μας και μείνετε ενήμεροι για την τεχνογνωσία από τον τομέα της πληροφορικής.

    Σχετικά με εμάς

    The Codest - Διεθνής εταιρεία ανάπτυξης λογισμικού με κέντρα τεχνολογίας στην Πολωνία.

    Ηνωμένο Βασίλειο - Έδρα

    • Γραφείο 303B, 182-184 High Street North E6 2JA
      Λονδίνο, Αγγλία

    Πολωνία - Τοπικοί κόμβοι τεχνολογίας

    • Πάρκο γραφείων Fabryczna, Aleja
      Pokoju 18, 31-564 Κρακοβία
    • Πρεσβεία του εγκεφάλου, Konstruktorska
      11, 02-673 Βαρσοβία, Πολωνία

      The Codest

    • Αρχική σελίδα
    • Σχετικά με εμάς
    • Υπηρεσίες
    • Case Studies
    • Μάθετε πώς
    • Καριέρα
    • Λεξικό

      Υπηρεσίες

    • Συμβουλευτική
    • Ανάπτυξη λογισμικού
    • Backend Ανάπτυξη
    • Ανάπτυξη Frontend
    • Staff Augmentation
    • Backend Developers
    • Μηχανικοί cloud
    • Μηχανικοί δεδομένων
    • Άλλα
    • Μηχανικοί QA

      Πόροι

    • Γεγονότα και μύθοι σχετικά με τη συνεργασία με εξωτερικό συνεργάτη ανάπτυξης λογισμικού
    • Από τις ΗΠΑ στην Ευρώπη: Γιατί οι αμερικανικές νεοσύστατες επιχειρήσεις αποφασίζουν να μετεγκατασταθούν στην Ευρώπη
    • Σύγκριση υπεράκτιων κόμβων ανάπτυξης τεχνολογίας: Ευρώπη (Πολωνία), ASEAN (Φιλιππίνες), Ευρασία (Τουρκία)
    • Ποιες είναι οι κορυφαίες προκλήσεις των CTOs και των CIOs;
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Πνευματικά δικαιώματα © 2025 από The Codest. Όλα τα δικαιώματα διατηρούνται.

    elGreek
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek