Java Concurrency - Kurzanleitung

Java ist eine Multithread-Programmiersprache, was bedeutet, dass wir Multithread-Programme mit Java entwickeln können. Ein Multithread-Programm enthält zwei oder mehr Teile, die gleichzeitig ausgeführt werden können, und jeder Teil kann gleichzeitig eine andere Aufgabe ausführen und die verfügbaren Ressourcen optimal nutzen, insbesondere wenn Ihr Computer über mehrere CPUs verfügt.

Multitasking ist per Definition, wenn mehrere Prozesse gemeinsame Verarbeitungsressourcen wie eine CPU gemeinsam nutzen. Multithreading erweitert die Idee des Multitasking auf Anwendungen, bei denen Sie bestimmte Vorgänge innerhalb einer einzelnen Anwendung in einzelne Threads unterteilen können. Jeder der Threads kann parallel ausgeführt werden. Das Betriebssystem teilt die Verarbeitungszeit nicht nur auf verschiedene Anwendungen auf, sondern auch auf jeden Thread innerhalb einer Anwendung.

Mit Multithreading können Sie so schreiben, dass mehrere Aktivitäten gleichzeitig im selben Programm ausgeführt werden können.

Lebenszyklus eines Threads

Ein Thread durchläuft verschiedene Phasen seines Lebenszyklus. Beispielsweise wird ein Thread geboren, gestartet, ausgeführt und stirbt dann. Das folgende Diagramm zeigt den gesamten Lebenszyklus eines Threads.

Es folgen die Phasen des Lebenszyklus -

  • New- Ein neuer Thread beginnt seinen Lebenszyklus im neuen Zustand. Es bleibt in diesem Zustand, bis das Programm den Thread startet. Es wird auch als bezeichnetborn thread.

  • Runnable- Nachdem ein neu geborener Thread gestartet wurde, kann der Thread ausgeführt werden. Ein Thread in diesem Status führt seine Aufgabe aus.

  • Waiting- Manchmal wechselt ein Thread in den Wartezustand, während der Thread darauf wartet, dass ein anderer Thread eine Aufgabe ausführt. Ein Thread wechselt nur dann in den ausführbaren Zustand zurück, wenn ein anderer Thread dem wartenden Thread signalisiert, die Ausführung fortzusetzen.

  • Timed Waiting- Ein ausführbarer Thread kann für ein bestimmtes Zeitintervall in den zeitgesteuerten Wartezustand wechseln. Ein Thread in diesem Zustand wechselt zurück in den ausführbaren Zustand, wenn dieses Zeitintervall abläuft oder wenn das Ereignis auftritt, auf das er wartet.

  • Terminated (Dead) - Ein ausführbarer Thread wechselt in den Status "Beendet", wenn er seine Aufgabe abgeschlossen oder auf andere Weise beendet hat.

Thread-Prioritäten

Jeder Java-Thread hat eine Priorität, mit deren Hilfe das Betriebssystem die Reihenfolge bestimmen kann, in der Threads geplant sind.

Java-Thread-Prioritäten liegen im Bereich zwischen MIN_PRIORITY (Konstante 1) und MAX_PRIORITY (Konstante 10). Standardmäßig erhält jeder Thread die Priorität NORM_PRIORITY (eine Konstante von 5).

Threads mit höherer Priorität sind für ein Programm wichtiger und sollten vor Threads mit niedrigerer Priorität Prozessorzeit zugewiesen bekommen. Thread-Prioritäten können jedoch nicht die Reihenfolge garantieren, in der Threads ausgeführt werden, und sind stark plattformabhängig.

Erstellen Sie einen Thread, indem Sie eine ausführbare Schnittstelle implementieren

Wenn Ihre Klasse als Thread ausgeführt werden soll, können Sie dies erreichen, indem Sie a implementieren RunnableSchnittstelle. Sie müssen drei grundlegende Schritte ausführen:

Schritt 1

Als ersten Schritt müssen Sie eine run () -Methode implementieren, die von a bereitgestellt wird RunnableSchnittstelle. Diese Methode bietet einen Einstiegspunkt für den Thread und Sie werden Ihre vollständige Geschäftslogik in diese Methode einfügen. Es folgt eine einfache Syntax der Methode run () -

public void run( )

Schritt 2

In einem zweiten Schritt instanziieren Sie a Thread Objekt mit dem folgenden Konstruktor -

Thread(Runnable threadObj, String threadName);

Wobei threadObj eine Instanz einer Klasse ist, die das implementiertRunnable Schnittstelle und threadName ist der Name des neuen Threads.

Schritt 3

Sobald ein Thread-Objekt erstellt wurde, können Sie es durch Aufrufen starten start()Methode, die einen Aufruf der run () -Methode ausführt. Es folgt eine einfache Syntax der start () -Methode -

void start();

Example

Hier ist ein Beispiel, das einen neuen Thread erstellt und ihn ausführt -

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;

   RunnableDemo(String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      
      try {
      
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo("Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo("Thread-2");
      R2.start();
   }   
}

Dies führt zu folgendem Ergebnis:

Output

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Erstellen Sie einen Thread, indem Sie eine Thread-Klasse erweitern

Die zweite Möglichkeit, einen Thread zu erstellen, besteht darin, eine neue Klasse zu erstellen, die erweitert wird ThreadKlasse mit den folgenden zwei einfachen Schritten. Dieser Ansatz bietet mehr Flexibilität bei der Verarbeitung mehrerer Threads, die mit verfügbaren Methoden in der Thread-Klasse erstellt wurden.

Schritt 1

Sie müssen überschreiben run( )Methode in der Thread-Klasse verfügbar. Diese Methode bietet einen Einstiegspunkt für den Thread und Sie werden Ihre vollständige Geschäftslogik in diese Methode einfügen. Es folgt eine einfache Syntax der run () -Methode -

public void run( )

Schritt 2

Sobald das Thread-Objekt erstellt wurde, können Sie es durch Aufrufen starten start()Methode, die einen Aufruf der run () -Methode ausführt. Es folgt eine einfache Syntax der start () -Methode -

void start( );

Example

Hier ist das vorhergehende Programm umgeschrieben, um den Thread zu erweitern -

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo(String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      
      try {

         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo("Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo("Thread-2");
      T2.start();
   }   
}

Dies führt zu folgendem Ergebnis:

Output

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

In diesem Kapitel werden die verschiedenen Aspekte der Einrichtung einer kongenialen Umgebung für Java erläutert.

Einrichtung der lokalen Umgebung

Wenn Sie weiterhin bereit sind, Ihre Umgebung für die Programmiersprache Java einzurichten, finden Sie in diesem Abschnitt Anleitungen zum Herunterladen und Einrichten von Java auf Ihrem Computer. Im Folgenden finden Sie die Schritte zum Einrichten der Umgebung.

Java SE ist frei verfügbar über den Link Java herunterladen . Sie können eine Version herunterladen, die auf Ihrem Betriebssystem basiert.

Befolgen Sie die Anweisungen, um Java herunterzuladen und auszuführen .exeum Java auf Ihrem Computer zu installieren. Sobald Sie Java auf Ihrem Computer installiert haben, müssen Sie Umgebungsvariablen festlegen, die auf korrekte Installationsverzeichnisse verweisen.

Einrichten des Pfads für Windows

Angenommen, Sie haben Java im Verzeichnis c: \ Programme \ java \ jdk installiert -

  • Klicken Sie mit der rechten Maustaste auf "Arbeitsplatz" und wählen Sie "Eigenschaften".

  • Klicken Sie auf der Registerkarte "Erweitert" auf die Schaltfläche "Umgebungsvariablen".

  • Ändern Sie nun die Variable 'Path' so, dass sie auch den Pfad zur ausführbaren Java-Datei enthält. Wenn der Pfad derzeit auf "C: \ WINDOWS \ SYSTEM32" festgelegt ist, ändern Sie Ihren Pfad in "C: \ WINDOWS \ SYSTEM32; c: \ Programme \ java \ jdk \ bin".

Einrichten des Pfads für Linux, UNIX, Solaris, FreeBSD

Die Umgebungsvariable PATH sollte so eingestellt sein, dass sie darauf verweist, wo die Java-Binärdateien installiert wurden. Informationen hierzu finden Sie in Ihrer Shell-Dokumentation.

Wenn Sie beispielsweise bash als Shell verwenden, fügen Sie die folgende Zeile am Ende Ihrer '.bashrc: export PATH = / path / to / java: $ PATH' hinzu.

Beliebte Java-Editoren

Zum Schreiben Ihrer Java-Programme benötigen Sie einen Texteditor. Es gibt noch ausgefeiltere IDEs auf dem Markt. Im Moment können Sie jedoch eine der folgenden Möglichkeiten in Betracht ziehen:

  • Notepad - Auf einem Windows-Computer können Sie einen einfachen Texteditor wie Notepad (für dieses Lernprogramm empfohlen) und TextPad verwenden.

  • Netbeans - Eine Java-IDE, die Open Source und kostenlos ist und von der heruntergeladen werden kann https://netbeans.org/index.html.

  • Eclipse - Eine Java-IDE, die von der Open-Source-Community von eclipse entwickelt wurde und von heruntergeladen werden kann https://www.eclipse.org/.

Core Java bietet vollständige Kontrolle über Multithread-Programme. Sie können ein Multithread-Programm entwickeln, das je nach Ihren Anforderungen angehalten, fortgesetzt oder vollständig gestoppt werden kann. Es gibt verschiedene statische Methoden, die Sie für Thread-Objekte verwenden können, um deren Verhalten zu steuern. In der folgenden Tabelle sind diese Methoden aufgeführt:

Sr.Nr. Methode & Beschreibung
1

public void suspend()

Diese Methode versetzt einen Thread in den angehaltenen Zustand und kann mit der Methode resume () fortgesetzt werden.

2

public void stop()

Diese Methode stoppt einen Thread vollständig.

3

public void resume()

Diese Methode setzt einen Thread fort, der mit der suspend () -Methode angehalten wurde.

4

public void wait()

Bewirkt, dass der aktuelle Thread wartet, bis ein anderer Thread notify () aufruft.

5

public void notify()

Weckt einen einzelnen Thread auf, der auf dem Monitor dieses Objekts wartet.

Beachten Sie, dass die neuesten Versionen von Java die Verwendung der Methoden suspend (), resume () und stop () veraltet haben und Sie daher verfügbare Alternativen verwenden müssen.

Beispiel

class RunnableDemo implements Runnable {
   public Thread t;
   private String threadName;
   boolean suspended = false;

   RunnableDemo(String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );

      try {
         
         for(int i = 10; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);

            // Let the thread sleep for a while.
            Thread.sleep(300);

            synchronized(this) {
               
               while(suspended) {
                  wait();
               }
            }
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
   
   void suspend() {
      suspended = true;
   }
   
   synchronized void resume() {
      suspended = false;
      notify();
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo("Thread-1");
      R1.start();

      RunnableDemo R2 = new RunnableDemo("Thread-2");
      R2.start();

      try {
         Thread.sleep(1000);
         R1.suspend();
         System.out.println("Suspending First Thread");
         Thread.sleep(1000);
         R1.resume();
         System.out.println("Resuming First Thread");
         
         R2.suspend();
         System.out.println("Suspending thread Two");
         Thread.sleep(1000);
         R2.resume();
         System.out.println("Resuming thread Two");
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      } try {
         System.out.println("Waiting for threads to finish.");
         R1.t.join();
         R2.t.join();
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      }
      System.out.println("Main thread exiting.");
   }
}

Das obige Programm erzeugt die folgende Ausgabe -

Ausgabe

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 10
Running Thread-2
Thread: Thread-2, 10
Thread: Thread-1, 9
Thread: Thread-2, 9
Thread: Thread-1, 8
Thread: Thread-2, 8
Thread: Thread-1, 7
Thread: Thread-2, 7
Suspending First Thread
Thread: Thread-2, 6
Thread: Thread-2, 5
Thread: Thread-2, 4
Resuming First Thread
Suspending thread Two
Thread: Thread-1, 6
Thread: Thread-1, 5
Thread: Thread-1, 4
Thread: Thread-1, 3
Resuming thread Two
Thread: Thread-2, 3
Waiting for threads to finish.
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Main thread exiting.

Wenn Sie sich der Interprozesskommunikation bewusst sind, können Sie die Interthread-Kommunikation leicht verstehen. Die Interthread-Kommunikation ist wichtig, wenn Sie eine Anwendung entwickeln, in der zwei oder mehr Threads Informationen austauschen.

Es gibt drei einfache Methoden und einen kleinen Trick, der die Thread-Kommunikation ermöglicht. Alle drei Methoden sind unten aufgeführt -

Sr.Nr. Methode & Beschreibung
1

public void wait()

Bewirkt, dass der aktuelle Thread wartet, bis ein anderer Thread notify () aufruft.

2

public void notify()

Weckt einen einzelnen Thread auf, der auf dem Monitor dieses Objekts wartet.

3

public void notifyAll()

Aktiviert alle Threads, die wait () für dasselbe Objekt aufgerufen haben.

Diese Methoden wurden implementiert als finalMethoden in Object, sodass sie in allen Klassen verfügbar sind. Alle drei Methoden können nur innerhalb von a aufgerufen werdensynchronized Kontext.

Beispiel

Dieses Beispiel zeigt, wie zwei Threads mit kommunizieren können wait() und notify()Methode. Mit demselben Konzept können Sie ein komplexes System erstellen.

class Chat {
   boolean flag = false;

   public synchronized void Question(String msg) {

      if (flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = true;
      notify();
   }

   public synchronized void Answer(String msg) {

      if (!flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = false;
      notify();
   }
}

class T1 implements Runnable {
   Chat m;
   String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" };

   public T1(Chat m1) {
      this.m = m1;
      new Thread(this, "Question").start();
   }

   public void run() {
   
      for (int i = 0; i < s1.length; i++) {
         m.Question(s1[i]);
      }
   }
}

class T2 implements Runnable {
   Chat m;
   String[] s2 = { "Hi", "I am good, what about you?", "Great!" };

   public T2(Chat m2) {
      this.m = m2;
      new Thread(this, "Answer").start();
   }

   public void run() {

      for (int i = 0; i < s2.length; i++) {
         m.Answer(s2[i]);
      }
   }
}

public class TestThread {

   public static void main(String[] args) {
      Chat m = new Chat();
      new T1(m);
      new T2(m);
   }
}

Wenn das obige Programm eingehalten und ausgeführt wird, führt es zu folgendem Ergebnis:

Ausgabe

Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!

Das obige Beispiel wurde aus [https://stackoverflow.com/questions/2170520/inter-thread-communication-in-java] übernommen und anschließend geändert.

Multithreading-Beispiel mit Synchronisation

Hier ist das gleiche Beispiel, das den Zählerwert nacheinander druckt und jedes Mal, wenn wir ihn ausführen, das gleiche Ergebnis liefert.

Beispiel

class PrintDemo {
   
   public void printCount() {
      
      try {
         
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo(String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      
      synchronized(PD) {
         PD.printCount();
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo T2 = new ThreadDemo("Thread - 2 ", PD);

      T1.start();
      T2.start();

      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Dies führt jedes Mal zum gleichen Ergebnis, wenn Sie dieses Programm ausführen -

Ausgabe

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

Deadlock beschreibt eine Situation, in der zwei oder mehr Threads für immer blockiert sind und aufeinander warten. Deadlock tritt auf, wenn mehrere Threads dieselben Sperren benötigen, diese jedoch in unterschiedlicher Reihenfolge erhalten. Ein Java-Multithread-Programm kann unter der Deadlock-Bedingung leiden, weil diesynchronizedDas Schlüsselwort bewirkt, dass der ausführende Thread blockiert, während auf die dem angegebenen Objekt zugeordnete Sperre oder Überwachung gewartet wird. Hier ist ein Beispiel.

Beispiel

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
   
      public void run() {
      
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");

            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }

   private static class ThreadDemo2 extends Thread {
   
      public void run() {
      
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            
            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Wenn Sie das obige Programm kompilieren und ausführen, stellen Sie eine Deadlock-Situation fest. Die folgende Ausgabe wird vom Programm erzeugt:

Ausgabe

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

Das obige Programm bleibt für immer hängen, da keiner der Threads in der Position ist, um fortzufahren und darauf zu warten, dass sich die Sperre aufhebt, sodass Sie das Programm verlassen können, indem Sie STRG + C drücken.

Beispiel für eine Deadlock-Lösung

Lassen Sie uns die Reihenfolge der Sperre ändern und dasselbe Programm ausführen, um festzustellen, ob beide Threads noch aufeinander warten.

Beispiel

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
   
      public void run() {
         
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }

   private static class ThreadDemo2 extends Thread {
      
      public void run() {
         
         synchronized (Lock1) {
            System.out.println("Thread 2: Holding lock 1...");
           
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Das Ändern der Reihenfolge der Sperren verhindert also, dass das Programm in eine Deadlock-Situation gerät, und führt zu folgendem Ergebnis:

Ausgabe

Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...

Das obige Beispiel soll nur das Konzept verdeutlichen. Es ist jedoch ein komplexes Konzept, und Sie sollten sich eingehend damit befassen, bevor Sie Ihre Anwendungen für Deadlock-Situationen entwickeln.

Die ThreadLocal-Klasse wird verwendet, um lokale Thread-Variablen zu erstellen, die nur von demselben Thread gelesen und geschrieben werden können. Wenn beispielsweise zwei Threads auf Code zugreifen, der auf dieselbe threadLocal-Variable verweist, werden für jeden Thread keine Änderungen an der threadLocal-Variablen vorgenommen, die von einem anderen Thread vorgenommen wurden.

ThreadLocal-Methoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der ThreadLocal-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public T get()

Gibt den Wert in der aktuellen Thread-Kopie dieser thread-lokalen Variablen zurück.

2

protected T initialValue()

Gibt den "Anfangswert" des aktuellen Threads für diese threadlokale Variable zurück.

3

public void remove()

Entfernt den Wert des aktuellen Threads für diese threadlokale Variable.

4

public void set(T value)

Setzt die Kopie dieser aktuellen threadlokalen Variablen des aktuellen Threads auf den angegebenen Wert.

Beispiel

Das folgende TestThread-Programm demonstriert einige dieser Methoden der ThreadLocal-Klasse. Hier haben wir zwei Zählervariablen verwendet, eine ist eine normale Variable und eine andere ist ThreadLocal.

class RunnableDemo implements Runnable {
   int counter;
   ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<Integer>();

   public void run() {     
      counter++;

      if(threadLocalCounter.get() != null) {
         threadLocalCounter.set(threadLocalCounter.get().intValue() + 1);
      } else {
         threadLocalCounter.set(0);
      }
      System.out.println("Counter: " + counter);
      System.out.println("threadLocalCounter: " + threadLocalCounter.get());
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo commonInstance = new RunnableDemo();

      Thread t1 = new Thread(commonInstance);
      Thread t2 = new Thread(commonInstance);
      Thread t3 = new Thread(commonInstance);
      Thread t4 = new Thread(commonInstance);

      t1.start();
      t2.start();
      t3.start();
      t4.start();

      // wait for threads to end
      try {
         t1.join();
         t2.join();
         t3.join();
         t4.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Counter: 1
threadLocalCounter: 0
Counter: 2
threadLocalCounter: 0
Counter: 3
threadLocalCounter: 0
Counter: 4
threadLocalCounter: 0

Sie können sehen, dass der Wert des Zählers von jedem Thread erhöht wird, threadLocalCounter jedoch für jeden Thread 0 bleibt.

Ein java.util.concurrent.ThreadLocalRandom ist eine Dienstprogrammklasse, die ab jdk 1.7 eingeführt wurde und nützlich ist, wenn mehrere Threads oder ForkJoinTasks erforderlich sind, um Zufallszahlen zu generieren. Es verbessert die Leistung und ist weniger umstritten als die Math.random () -Methode.

ThreadLocalRandom-Methoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der ThreadLocalRandom-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public static ThreadLocalRandom current()

Gibt das ThreadLocalRandom des aktuellen Threads zurück.

2

protected int next(int bits)

Erzeugt die nächste Pseudozufallszahl.

3

public double nextDouble(double n)

Gibt einen pseudozufälligen, gleichmäßig verteilten Doppelwert zwischen 0 (einschließlich) und dem angegebenen Wert (exklusiv) zurück.

4

public double nextDouble(double least, double bound)

Gibt einen pseudozufälligen, gleichmäßig verteilten Wert zwischen dem angegebenen kleinsten Wert (einschließlich) und gebunden (exklusiv) zurück.

5

public int nextInt(int least, int bound)

Gibt einen pseudozufälligen, gleichmäßig verteilten Wert zwischen dem angegebenen kleinsten Wert (einschließlich) und gebunden (exklusiv) zurück.

6

public long nextLong(long n)

Gibt einen pseudozufälligen, gleichmäßig verteilten Wert zwischen 0 (einschließlich) und dem angegebenen Wert (exklusiv) zurück.

7

public long nextLong(long least, long bound)

Gibt einen pseudozufälligen, gleichmäßig verteilten Wert zwischen dem angegebenen kleinsten Wert (einschließlich) und gebunden (exklusiv) zurück.

8

public void setSeed(long seed)

Löst eine nicht unterstützte OperationException aus.

Beispiel

Das folgende TestThread-Programm demonstriert einige dieser Methoden der Lock-Schnittstelle. Hier haben wir lock () verwendet, um das Schloss zu erhalten, und lock (), um das Schloss aufzuheben.

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ThreadLocalRandom;

public class TestThread {
  
   public static void main(final String[] arguments) {
      System.out.println("Random Integer: " + new Random().nextInt());  
      System.out.println("Seeded Random Integer: " + new Random(15).nextInt());  
      System.out.println(
         "Thread Local Random Integer: " + ThreadLocalRandom.current().nextInt());
      
      final ThreadLocalRandom random = ThreadLocalRandom.current();  
      random.setSeed(15); //exception will come as seeding is not allowed in ThreadLocalRandom.
      System.out.println("Seeded Thread Local Random Integer: " + random.nextInt());  
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Random Integer: 1566889198
Seeded Random Integer: -1159716814
Thread Local Random Integer: 358693993
Exception in thread "main" java.lang.UnsupportedOperationException
        at java.util.concurrent.ThreadLocalRandom.setSeed(Unknown Source)
        at TestThread.main(TestThread.java:21)

Hier haben wir ThreadLocalRandom- und Random-Klassen verwendet, um Zufallszahlen zu erhalten.

Eine java.util.concurrent.locks.Lock-Schnittstelle wird als Thread-Synchronisationsmechanismus verwendet, der synchronisierten Blöcken ähnelt. Der neue Verriegelungsmechanismus ist flexibler und bietet mehr Optionen als ein synchronisierter Block. Die Hauptunterschiede zwischen einer Sperre und einem synchronisierten Block sind folgende:

  • Guarantee of sequence- Der synchronisierte Block bietet keine Garantie für die Reihenfolge, in der der wartende Thread Zugriff erhält. Lock-Schnittstelle behandelt es.

  • No timeout- Der synchronisierte Block hat keine Zeitüberschreitungsoption, wenn keine Sperre gewährt wird. Die Sperrschnittstelle bietet eine solche Option.

  • Single method - Der synchronisierte Block muss vollständig in einer einzelnen Methode enthalten sein, während die Methoden lock () und refresh () einer Sperrschnittstelle in verschiedenen Methoden aufgerufen werden können.

Sperrmethoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der Lock-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public void lock()

Erwirbt das Schloss.

2

public void lockInterruptibly()

Erhält die Sperre, sofern der aktuelle Thread nicht unterbrochen wird.

3

public Condition newCondition()

Gibt eine neue Bedingungsinstanz zurück, die an diese Sperrinstanz gebunden ist.

4

public boolean tryLock()

Erwirbt die Sperre nur, wenn sie zum Zeitpunkt des Aufrufs frei ist.

5

public boolean tryLock()

Erwirbt die Sperre nur, wenn sie zum Zeitpunkt des Aufrufs frei ist.

6

public boolean tryLock(long time, TimeUnit unit)

Erwirbt die Sperre, wenn sie innerhalb der angegebenen Wartezeit frei ist und der aktuelle Thread nicht unterbrochen wurde.

7

public void unlock()

Löst die Sperre auf.

Beispiel

Das folgende TestThread-Programm demonstriert einige dieser Methoden der Lock-Schnittstelle. Hier haben wir lock () verwendet, um das Schloss zu erhalten, und lock (), um das Schloss aufzuheben.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class PrintDemo {
   private final Lock queueLock = new ReentrantLock();

   public void print() {
      queueLock.lock();

      try {
         Long duration = (long) (Math.random() * 10000);
         System.out.println(Thread.currentThread().getName() 
            + "  Time Taken " + (duration / 1000) + " seconds.");
         Thread.sleep(duration);
      } catch (InterruptedException e) {
         e.printStackTrace();
      } finally {
         System.out.printf(
            "%s printed the document successfully.\n", Thread.currentThread().getName());
         queueLock.unlock();
      }
   }
}

class ThreadDemo extends Thread {
   PrintDemo  printDemo;

   ThreadDemo(String name,  PrintDemo printDemo) {
      super(name);
      this.printDemo = printDemo;
   }   

   @Override
   public void run() {
      System.out.printf(
         "%s starts printing a document\n", Thread.currentThread().getName());
      printDemo.print();
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo t1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo t2 = new ThreadDemo("Thread - 2 ", PD);
      ThreadDemo t3 = new ThreadDemo("Thread - 3 ", PD);
      ThreadDemo t4 = new ThreadDemo("Thread - 4 ", PD);

      t1.start();
      t2.start();
      t3.start();
      t4.start();
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Thread - 1  starts printing a document
Thread - 4  starts printing a document
Thread - 3  starts printing a document
Thread - 2  starts printing a document
Thread - 1   Time Taken 4 seconds.
Thread - 1  printed the document successfully.
Thread - 4   Time Taken 3 seconds.
Thread - 4  printed the document successfully.
Thread - 3   Time Taken 5 seconds.
Thread - 3  printed the document successfully.
Thread - 2   Time Taken 4 seconds.
Thread - 2  printed the document successfully.

Wir haben hier die ReentrantLock-Klasse als Implementierung der Lock-Schnittstelle verwendet. Mit der ReentrantLock-Klasse kann ein Thread eine Methode sperren, auch wenn bereits eine andere Methode gesperrt ist.

Eine java.util.concurrent.locks.ReadWriteLock-Schnittstelle ermöglicht das gleichzeitige Lesen mehrerer Threads, es kann jedoch jeweils nur ein Thread schreiben.

  • Read Lock - Wenn kein Thread das ReadWriteLock zum Schreiben gesperrt hat, können mehrere Threads auf die Lesesperre zugreifen.

  • Write Lock - Wenn kein Thread liest oder schreibt, kann ein Thread auf die Schreibsperre zugreifen.

Sperrmethoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der Lock-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public Lock readLock()

Gibt die zum Lesen verwendete Sperre zurück.

2

public Lock writeLock()

Gibt die zum Schreiben verwendete Sperre zurück.

Beispiel

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TestThread {
   private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
   private static String message = "a";

   public static void main(String[] args) throws InterruptedException {
      Thread t1 = new Thread(new WriterA());
      t1.setName("Writer A");
      
      Thread t2 = new Thread(new WriterB());
      t2.setName("Writer B");
      
      Thread t3 = new Thread(new Reader());
      t3.setName("Reader");
      t1.start();
      t2.start();
      t3.start();
      t1.join();
      t2.join();
      t3.join();
   }

   static class Reader implements Runnable {

      public void run() {
         
         if(lock.isWriteLocked()) {
            System.out.println("Write Lock Present.");
         }
         lock.readLock().lock();

         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            System.out.println(Thread.currentThread().getName() +": "+ message );
            lock.readLock().unlock();
         }
      }
   }

   static class WriterA implements Runnable {

      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("a");
            lock.writeLock().unlock();
         }
      }
   }

   static class WriterB implements Runnable {

      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("b");
            lock.writeLock().unlock();
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Writer A  Time Taken 6 seconds.
Write Lock Present.
Writer B  Time Taken 2 seconds.
Reader  Time Taken 0 seconds.
Reader: aab

Eine Schnittstelle java.util.concurrent.locks.Condition bietet eine Thread-Funktion, mit der die Ausführung angehalten werden kann, bis die angegebene Bedingung erfüllt ist. Ein Condition-Objekt ist notwendigerweise an eine Sperre gebunden und muss mit der newCondition () -Methode abgerufen werden.

Bedingungsmethoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der Condition-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public void await()

Bewirkt, dass der aktuelle Thread wartet, bis er signalisiert oder unterbrochen wird.

2

public boolean await(long time, TimeUnit unit)

Bewirkt, dass der aktuelle Thread wartet, bis er signalisiert oder unterbrochen wird oder die angegebene Wartezeit abgelaufen ist.

3

public long awaitNanos(long nanosTimeout)

Bewirkt, dass der aktuelle Thread wartet, bis er signalisiert oder unterbrochen wird oder die angegebene Wartezeit abgelaufen ist.

4

public long awaitUninterruptibly()

Bewirkt, dass der aktuelle Thread wartet, bis er signalisiert wird.

5

public long awaitUntil()

Bewirkt, dass der aktuelle Thread wartet, bis er signalisiert oder unterbrochen wird oder die angegebene Frist abgelaufen ist.

6

public void signal()

Weckt einen wartenden Thread auf.

7

public void signalAll()

Weckt alle wartenden Threads auf.

Beispiel

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestThread {

   public static void main(String[] args) throws InterruptedException {
      ItemQueue itemQueue = new ItemQueue(10);

      //Create a producer and a consumer.
      Thread producer = new Producer(itemQueue);
      Thread consumer = new Consumer(itemQueue);

      //Start both threads.
      producer.start();
      consumer.start();

      //Wait for both threads to terminate.
      producer.join();
      consumer.join();
   }

   static class ItemQueue {
      private Object[] items = null;
      private int current = 0;
      private int placeIndex = 0;
      private int removeIndex = 0;

      private final Lock lock;
      private final Condition isEmpty;
      private final Condition isFull;

      public ItemQueue(int capacity) {
         this.items = new Object[capacity];
         lock = new ReentrantLock();
         isEmpty = lock.newCondition();
         isFull = lock.newCondition();
      }

      public void add(Object item) throws InterruptedException {
         lock.lock();

         while(current >= items.length)
            isFull.await();

         items[placeIndex] = item;
         placeIndex = (placeIndex + 1) % items.length;
         ++current;

         //Notify the consumer that there is data available.
         isEmpty.signal();
         lock.unlock();
      }

      public Object remove() throws InterruptedException {
         Object item = null;

         lock.lock();

         while(current <= 0) {
            isEmpty.await();
         }
         item = items[removeIndex];
         removeIndex = (removeIndex + 1) % items.length;
         --current;

         //Notify the producer that there is space available.
         isFull.signal();
         lock.unlock();

         return item;
      }

      public boolean isEmpty() {
         return (items.length == 0);
      }
   }

   static class Producer extends Thread {
      private final ItemQueue queue;
      
      public Producer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         String[] numbers =
            {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};

         try {
            
            for(String number: numbers) {
               System.out.println("[Producer]: " + number);
            }
            queue.add(null);
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         } 
      }
   }

   static class Consumer extends Thread {
      private final ItemQueue queue;
      
      public Consumer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         
         try {
            
            do {
               Object number = queue.remove();
               System.out.println("[Consumer]: " + number);

               if(number == null) {
                  return;
               }
            } while(!queue.isEmpty());
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

[Producer]: 1
[Producer]: 2
[Producer]: 3
[Producer]: 4
[Producer]: 5
[Producer]: 6
[Producer]: 7
[Producer]: 8
[Producer]: 9
[Producer]: 10
[Producer]: 11
[Producer]: 12
[Consumer]: null

Eine java.util.concurrent.atomic.AtomicInteger-Klasse bietet Operationen für den zugrunde liegenden int-Wert, die atomar gelesen und geschrieben werden können, und enthält auch erweiterte atomare Operationen. AtomicInteger unterstützt atomare Operationen für die zugrunde liegende int-Variable. Es verfügt über Methoden zum Abrufen und Festlegen, die wie das Lesen und Schreiben flüchtiger Variablen funktionieren. Das heißt, eine Menge hat eine Vorher-Beziehung zu einem nachfolgenden Abruf derselben Variablen. Die atomare compareAndSet-Methode verfügt auch über diese Speicherkonsistenzfunktionen.

AtomicInteger-Methoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der AtomicInteger-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public int addAndGet(int delta)

Addiert atomar den angegebenen Wert zum aktuellen Wert.

2

public boolean compareAndSet(int expect, int update)

Setzt den Wert atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert dem erwarteten Wert entspricht.

3

public int decrementAndGet()

Verringert den aktuellen Wert atomar um eins.

4

public double doubleValue()

Gibt den Wert der angegebenen Zahl als Double zurück.

5

public float floatValue()

Gibt den Wert der angegebenen Zahl als Float zurück.

6

public int get()

Ruft den aktuellen Wert ab.

7

public int getAndAdd(int delta)

Atomiclly addiert den angegebenen Wert zum aktuellen Wert.

8

public int getAndDecrement()

Verringert den aktuellen Wert atomar um eins.

9

public int getAndIncrement()

Erhöht den aktuellen Wert atomar um eins.

10

public int getAndSet(int newValue)

Setzt atomar auf den angegebenen Wert und gibt den alten Wert zurück.

11

public int incrementAndGet()

Erhöht den aktuellen Wert atomar um eins.

12

public int intValue()

Gibt den Wert der angegebenen Zahl als int zurück.

13

public void lazySet(int newValue)

Setzt schließlich auf den angegebenen Wert.

14

public long longValue()

Gibt den Wert der angegebenen Zahl als long zurück.

15

public void set(int newValue)

Setzt auf den angegebenen Wert.

16

public String toString()

Gibt die String-Darstellung des aktuellen Werts zurück.

17

public boolean weakCompareAndSet(int expect, int update)

Setzt den Wert atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert dem erwarteten Wert entspricht.

Beispiel

Das folgende TestThread-Programm zeigt eine unsichere Implementierung des Zählers in einer threadbasierten Umgebung.

public class TestThread {

   static class Counter {
      private int c = 0;

      public void increment() {
         c++;
      }

      public int value() {
         return c;
      }
   }
   
   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {
         
         new Thread(new Runnable() {
            
            public void run() {
               counter.increment();
            }
         }).start(); 
      }  
      Thread.sleep(6000);
      System.out.println("Final number (should be 1000): " + counter.value());
   }  
}

Dies kann abhängig von der Geschwindigkeit des Computers und der Thread-Verschachtelung zu folgendem Ergebnis führen.

Ausgabe

Final number (should be 1000): 1000

Beispiel

import java.util.concurrent.atomic.AtomicInteger;

public class TestThread {

   static class Counter {
      private AtomicInteger c = new AtomicInteger(0);

      public void increment() {
         c.getAndIncrement();
      }

      public int value() {
         return c.get();
      }
   }
   
   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {

         new Thread(new Runnable() {
            public void run() {
               counter.increment();
            }
         }).start(); 
      }  
      Thread.sleep(6000);
      System.out.println("Final number (should be 1000): " + counter.value());
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Final number (should be 1000): 1000

Eine java.util.concurrent.atomic.AtomicLong-Klasse bietet Operationen für zugrunde liegende Long-Werte, die atomar gelesen und geschrieben werden können, und enthält auch erweiterte atomare Operationen. AtomicLong unterstützt atomare Operationen für zugrunde liegende Long-Variablen. Es verfügt über Methoden zum Abrufen und Festlegen, die wie das Lesen und Schreiben flüchtiger Variablen funktionieren. Das heißt, eine Menge hat eine Vorher-Beziehung zu einem nachfolgenden Abruf derselben Variablen. Die atomare compareAndSet-Methode verfügt auch über diese Speicherkonsistenzfunktionen.

AtomicLong-Methoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der AtomicLong-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public long addAndGet(long delta)

Addiert atomar den angegebenen Wert zum aktuellen Wert.

2

public boolean compareAndSet(long expect, long update)

Setzt den Wert atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert dem erwarteten Wert entspricht.

3

public long decrementAndGet()

Verringert den aktuellen Wert atomar um eins.

4

public double doubleValue()

Gibt den Wert der angegebenen Zahl als Double zurück.

5

public float floatValue()

Gibt den Wert der angegebenen Zahl als Float zurück.

6

public long get()

Ruft den aktuellen Wert ab.

7

public long getAndAdd(long delta)

Atomiclly addiert den angegebenen Wert zum aktuellen Wert.

8

public long getAndDecrement()

Verringert den aktuellen Wert atomar um eins.

9

public long getAndIncrement()

Erhöht den aktuellen Wert atomar um eins.

10

public long getAndSet(long newValue)

Setzt atomar auf den angegebenen Wert und gibt den alten Wert zurück.

11

public long incrementAndGet()

Erhöht den aktuellen Wert atomar um eins.

12

public int intValue()

Gibt den Wert der angegebenen Zahl als int zurück.

13

public void lazySet(long newValue)

Setzt schließlich auf den angegebenen Wert.

14

public long longValue()

Gibt den Wert der angegebenen Zahl als long zurück.

15

public void set(long newValue)

Setzt auf den angegebenen Wert.

16

public String toString()

Gibt die String-Darstellung des aktuellen Werts zurück.

17

public boolean weakCompareAndSet(long expect, long update)

Setzt den Wert atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert dem erwarteten Wert entspricht.

Beispiel

Das folgende TestThread-Programm zeigt eine sichere Implementierung des Zählers mit AtomicLong in einer threadbasierten Umgebung.

import java.util.concurrent.atomic.AtomicLong;

public class TestThread {

   static class Counter {
      private AtomicLong c = new AtomicLong(0);

      public void increment() {
         c.getAndIncrement();
      }

      public long value() {
         return c.get();
      }
   }

   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {
         
         new Thread(new Runnable() {
            
            public void run() {
               counter.increment();
            }
         }).start();	
      }
      Thread.sleep(6000);			   		  
      System.out.println("Final number (should be 1000): " + counter.value());
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Final number (should be 1000): 1000

Eine java.util.concurrent.atomic.AtomicBoolean-Klasse bietet Operationen für den zugrunde liegenden booleschen Wert, die atomar gelesen und geschrieben werden können, und enthält auch erweiterte atomare Operationen. AtomicBoolean unterstützt atomare Operationen für zugrunde liegende boolesche Variablen. Es verfügt über Methoden zum Abrufen und Festlegen, die wie das Lesen und Schreiben flüchtiger Variablen funktionieren. Das heißt, eine Menge hat eine Vorher-Beziehung zu einem nachfolgenden Abruf derselben Variablen. Die atomare compareAndSet-Methode verfügt auch über diese Speicherkonsistenzfunktionen.

AtomicBoolean-Methoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der AtomicBoolean-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public boolean compareAndSet(boolean expect, boolean update)

Setzt den Wert atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

2

public boolean get()

Gibt den aktuellen Wert zurück.

3

public boolean getAndSet(boolean newValue)

Setzt atomar auf den angegebenen Wert und gibt den vorherigen Wert zurück.

4

public void lazySet(boolean newValue)

Setzt schließlich auf den angegebenen Wert.

5

public void set(boolean newValue)

Wird unbedingt auf den angegebenen Wert gesetzt.

6

public String toString()

Gibt die String-Darstellung des aktuellen Werts zurück.

7

public boolean weakCompareAndSet(boolean expect, boolean update)

Setzt den Wert atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der AtomicBoolean-Variablen in einer threadbasierten Umgebung.

import java.util.concurrent.atomic.AtomicBoolean;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final AtomicBoolean atomicBoolean = new AtomicBoolean(false);

      new Thread("Thread 1") {

         public void run() {

            while(true) {
               System.out.println(Thread.currentThread().getName() 
                  +" Waiting for Thread 2 to set Atomic variable to true. Current value is "
                  + atomicBoolean.get());

               if(atomicBoolean.compareAndSet(true, false)) {
                  System.out.println("Done!");
                  break;
               }
            }
         };
      }.start();

      new Thread("Thread 2") {

         public void run() {
            System.out.println(Thread.currentThread().getName() +
               ", Atomic Variable: " +atomicBoolean.get()); 
            System.out.println(Thread.currentThread().getName() +
               " is setting the variable to true ");
            atomicBoolean.set(true);
            System.out.println(Thread.currentThread().getName() +
               ", Atomic Variable: " +atomicBoolean.get()); 
         };
      }.start();
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2, Atomic Variable: false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2 is setting the variable to true
Thread 2, Atomic Variable: true
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Done!

Eine java.util.concurrent.atomic.AtomicReference-Klasse bietet Operationen für zugrunde liegende Objektreferenzen, die atomar gelesen und geschrieben werden können, und enthält auch erweiterte atomare Operationen. AtomicReference unterstützt atomare Operationen für die zugrunde liegende Objektreferenzvariable. Es verfügt über Methoden zum Abrufen und Festlegen, die wie das Lesen und Schreiben flüchtiger Variablen funktionieren. Das heißt, eine Menge hat eine Vorher-Beziehung zu einem nachfolgenden Abruf derselben Variablen. Die atomare compareAndSet-Methode verfügt auch über diese Speicherkonsistenzfunktionen.

AtomicReference-Methoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der AtomicReference-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public boolean compareAndSet(V expect, V update)

Setzt den Wert atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

2

public boolean get()

Gibt den aktuellen Wert zurück.

3

public boolean getAndSet(V newValue)

Setzt atomar auf den angegebenen Wert und gibt den vorherigen Wert zurück.

4

public void lazySet(V newValue)

Setzt schließlich auf den angegebenen Wert.

5

public void set(V newValue)

Wird unbedingt auf den angegebenen Wert gesetzt.

6

public String toString()

Gibt die String-Darstellung des aktuellen Werts zurück.

7

public boolean weakCompareAndSet(V expect, V update)

Setzt den Wert atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der AtomicReference-Variablen in einer threadbasierten Umgebung.

import java.util.concurrent.atomic.AtomicReference;

public class TestThread {
   private static String message = "hello";
   private static AtomicReference<String> atomicReference;

   public static void main(final String[] arguments) throws InterruptedException {
      atomicReference = new AtomicReference<String>(message);
      
      new Thread("Thread 1") {
         
         public void run() {
            atomicReference.compareAndSet(message, "Thread 1");
            message = message.concat("-Thread 1!");
         };
      }.start();

      System.out.println("Message is: " + message);
      System.out.println("Atomic Reference of Message is: " + atomicReference.get());
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Message is: hello
Atomic Reference of Message is: Thread 1

Eine java.util.concurrent.atomic.AtomicIntegerArray-Klasse bietet Operationen für das zugrunde liegende int-Array, die atomar gelesen und geschrieben werden können, und enthält auch erweiterte atomare Operationen. AtomicIntegerArray unterstützt atomare Operationen für zugrunde liegende int-Array-Variablen. Es verfügt über Methoden zum Abrufen und Festlegen, die wie das Lesen und Schreiben flüchtiger Variablen funktionieren. Das heißt, eine Menge hat eine Vorher-Beziehung zu einem nachfolgenden Abruf derselben Variablen. Die atomare compareAndSet-Methode verfügt auch über diese Speicherkonsistenzfunktionen.

AtomicIntegerArray-Methoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der AtomicIntegerArray-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public int addAndGet(int i, int delta)

Addiert atomar den angegebenen Wert zum Element am Index i.

2

public boolean compareAndSet(int i, int expect, int update)

Setzt das Element an Position i atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

3

public int decrementAndGet(int i)

Dekrementiert das Element am Index i atomar um eins.

4

public int get(int i)

Ruft den aktuellen Wert an Position i ab.

5

public int getAndAdd(int i, int delta)

Addiert atomar den angegebenen Wert zum Element am Index i.

6

public int getAndDecrement(int i)

Dekrementiert das Element am Index i atomar um eins.

7

public int getAndIncrement(int i)

Inkrementiert das Element am Index i atomar um eins.

8

public int getAndSet(int i, int newValue)

Setzt das Element an Position i atomar auf den angegebenen Wert und gibt den alten Wert zurück.

9

public int incrementAndGet(int i)

Inkrementiert das Element am Index i atomar um eins.

10

public void lazySet(int i, int newValue)

Setzt das Element schließlich an Position i auf den angegebenen Wert.

11

public int length()

Gibt die Länge des Arrays zurück.

12

public void set(int i, int newValue)

Setzt das Element an Position i auf den angegebenen Wert.

13

public String toString()

Gibt die String-Darstellung der aktuellen Werte des Arrays zurück.

14

public boolean weakCompareAndSet(int i, int expect, int update)

Setzt das Element an Position i atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der AtomicIntegerArray-Variablen in einer threadbasierten Umgebung.

import java.util.concurrent.atomic.AtomicIntegerArray;

public class TestThread {
   private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);

   public static void main(final String[] arguments) throws InterruptedException {
      
      for (int i = 0; i<atomicIntegerArray.length(); i++) {
         atomicIntegerArray.set(i, 1);
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();

      System.out.println("Values: ");

      for (int i = 0; i<atomicIntegerArray.length(); i++) {
         System.out.print(atomicIntegerArray.get(i) + " ");
      }
   }

   static class Increment implements Runnable {

      public void run() {

         for(int i = 0; i<atomicIntegerArray.length(); i++) {
            int add = atomicIntegerArray.incrementAndGet(i);
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {

      public void run() {

         for(int i = 0; i<atomicIntegerArray.length(); i++) {
            boolean swapped = atomicIntegerArray.compareAndSet(i, 2, 3);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId()
                  + ", index " +i + ", value: 3");
            }
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Thread 10, index 0, value: 2
Thread 10, index 1, value: 2
Thread 10, index 2, value: 2
Thread 11, index 0, value: 3
Thread 10, index 3, value: 2
Thread 11, index 1, value: 3
Thread 11, index 2, value: 3
Thread 10, index 4, value: 2
Thread 11, index 3, value: 3
Thread 10, index 5, value: 2
Thread 10, index 6, value: 2
Thread 11, index 4, value: 3
Thread 10, index 7, value: 2
Thread 11, index 5, value: 3
Thread 10, index 8, value: 2
Thread 11, index 6, value: 3
Thread 10, index 9, value: 2
Thread 11, index 7, value: 3
Thread 11, index 8, value: 3
Thread 11, index 9, value: 3
Values:
3 3 3 3 3 3 3 3 3 3

Eine java.util.concurrent.atomic.AtomicLongArray-Klasse bietet Operationen für das zugrunde liegende lange Array, die atomar gelesen und geschrieben werden können, und enthält auch erweiterte atomare Operationen. AtomicLongArray unterstützt atomare Operationen für zugrunde liegende Long-Array-Variablen. Es verfügt über Methoden zum Abrufen und Festlegen, die wie das Lesen und Schreiben flüchtiger Variablen funktionieren. Das heißt, eine Menge hat eine Vorher-Beziehung zu einem nachfolgenden Abruf derselben Variablen. Die atomare compareAndSet-Methode verfügt auch über diese Speicherkonsistenzfunktionen.

AtomicLongArray-Methoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der AtomicLongArray-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public long addAndGet(int i, long delta)

Addiert atomar den angegebenen Wert zum Element am Index i.

2

public boolean compareAndSet(int i, long expect, long update)

Setzt das Element an Position i atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

3

public long decrementAndGet(int i)

Dekrementiert das Element am Index i atomar um eins.

4

public long get(int i)

Ruft den aktuellen Wert an Position i ab.

5

public long getAndAdd(int i, long delta)

Addiert atomar den angegebenen Wert zum Element am Index i.

6

public long getAndDecrement(int i)

Dekrementiert das Element am Index i atomar um eins.

7

public long getAndIncrement(int i)

Inkrementiert das Element am Index i atomar um eins.

8

public long getAndSet(int i, long newValue)

Setzt das Element an Position i atomar auf den angegebenen Wert und gibt den alten Wert zurück.

9

public long incrementAndGet(int i)

Inkrementiert das Element am Index i atomar um eins.

10

public void lazySet(int i, long newValue)

Setzt das Element schließlich an Position i auf den angegebenen Wert.

11

public int length()

Gibt die Länge des Arrays zurück.

12

public void set(int i, long newValue)

Setzt das Element an Position i auf den angegebenen Wert.

13

public String toString()

Gibt die String-Darstellung der aktuellen Werte des Arrays zurück.

14

public boolean weakCompareAndSet(int i, long expect, long update)

Setzt das Element an Position i atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der AtomicIntegerArray-Variablen in einer threadbasierten Umgebung.

import java.util.concurrent.atomic.AtomicLongArray;

public class TestThread {
   private static AtomicLongArray atomicLongArray = new AtomicLongArray(10);

   public static void main(final String[] arguments) throws InterruptedException {

      for (int i = 0; i<atomicLongArray.length(); i++) {
         atomicLongArray.set(i, 1);
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();

      System.out.println("Values: ");
      
      for (int i = 0; i<atomicLongArray.length(); i++) {
         System.out.print(atomicLongArray.get(i) + " ");
      }
   }  

   static class Increment implements Runnable {

      public void run() {

         for(int i = 0; i<atomicLongArray.length(); i++) {
            long add = atomicLongArray.incrementAndGet(i);
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {

      public void run() {

         for(int i = 0; i<atomicLongArray.length(); i++) {
            boolean swapped = atomicLongArray.compareAndSet(i, 2, 3);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId()
                  + ", index " +i + ", value: 3");
            }
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Thread 9, index 0, value: 2
Thread 10, index 0, value: 3
Thread 9, index 1, value: 2
Thread 9, index 2, value: 2
Thread 9, index 3, value: 2
Thread 9, index 4, value: 2
Thread 10, index 1, value: 3
Thread 9, index 5, value: 2
Thread 10, index 2, value: 3
Thread 9, index 6, value: 2
Thread 10, index 3, value: 3
Thread 9, index 7, value: 2
Thread 10, index 4, value: 3
Thread 9, index 8, value: 2
Thread 9, index 9, value: 2
Thread 10, index 5, value: 3
Thread 10, index 6, value: 3
Thread 10, index 7, value: 3
Thread 10, index 8, value: 3
Thread 10, index 9, value: 3
Values: 
3 3 3 3 3 3 3 3 3 3

Eine java.util.concurrent.atomic.AtomicReferenceArray-Klasse bietet Operationen für das zugrunde liegende Referenzarray, die atomar gelesen und geschrieben werden können, und enthält auch erweiterte atomare Operationen. AtomicReferenceArray unterstützt atomare Operationen für zugrunde liegende Referenzarrayvariablen. Es verfügt über Methoden zum Abrufen und Festlegen, die wie das Lesen und Schreiben flüchtiger Variablen funktionieren. Das heißt, eine Menge hat eine Vorher-Beziehung zu einem nachfolgenden Abruf derselben Variablen. Die atomare compareAndSet-Methode verfügt auch über diese Speicherkonsistenzfunktionen.

AtomicReferenceArray-Methoden

Im Folgenden finden Sie eine Liste wichtiger Methoden, die in der AtomicReferenceArray-Klasse verfügbar sind.

Sr.Nr. Methode & Beschreibung
1

public boolean compareAndSet(int i, E expect, E update)

Setzt das Element an Position i atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

2

public E get(int i)

Ruft den aktuellen Wert an Position i ab.

3

public E getAndSet(int i, E newValue)

Setzt das Element an Position i atomar auf den angegebenen Wert und gibt den alten Wert zurück.

4

public void lazySet(int i, E newValue)

Setzt das Element schließlich an Position i auf den angegebenen Wert.

5

public int length()

Gibt die Länge des Arrays zurück.

6

public void set(int i, E newValue)

Setzt das Element an Position i auf den angegebenen Wert.

7

public String toString()

Gibt die String-Darstellung der aktuellen Werte des Arrays zurück.

8

public boolean weakCompareAndSet(int i, E expect, E update)

Setzt das Element an Position i atomar auf den angegebenen aktualisierten Wert, wenn der aktuelle Wert == der erwartete Wert ist.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der AtomicReferenceArray-Variablen in einer threadbasierten Umgebung.

import java.util.concurrent.atomic.AtomicReferenceArray;

public class TestThread {
   private static String[] source = new String[10];
   private static AtomicReferenceArray<String> atomicReferenceArray 
      = new AtomicReferenceArray<String>(source);

   public static void main(final String[] arguments) throws InterruptedException {

      for (int i = 0; i<atomicReferenceArray.length(); i++) {
         atomicReferenceArray.set(i, "item-2");
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();		
   }  

   static class Increment implements Runnable {
      
      public void run() {
         
         for(int i = 0; i<atomicReferenceArray.length(); i++) {
            String add = atomicReferenceArray.getAndSet(i,"item-"+ (i+1));
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {
      
      public void run() {
         
         for(int i = 0; i<atomicReferenceArray.length(); i++) {
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ atomicReferenceArray.get(i));
            boolean swapped = atomicReferenceArray.compareAndSet(i, "item-2", "updated-item-2");
            System.out.println("Item swapped: " + swapped);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId() 
                  + ", index " +i + ", updated-item-2");
            }
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Thread 9, index 0, value: item-2
Thread 10, index 0, value: item-1
Item swapped: false
Thread 10, index 1, value: item-2
Item swapped: true
Thread 9, index 1, value: updated-item-2
Thread 10, index 1, updated-item-2
Thread 10, index 2, value: item-3
Item swapped: false
Thread 10, index 3, value: item-2
Item swapped: true
Thread 10, index 3, updated-item-2
Thread 10, index 4, value: item-2
Item swapped: true
Thread 10, index 4, updated-item-2
Thread 10, index 5, value: item-2
Item swapped: true
Thread 10, index 5, updated-item-2
Thread 10, index 6, value: item-2
Thread 9, index 2, value: item-2
Item swapped: true
Thread 9, index 3, value: updated-item-2
Thread 10, index 6, updated-item-2
Thread 10, index 7, value: item-2
Thread 9, index 4, value: updated-item-2
Item swapped: true
Thread 9, index 5, value: updated-item-2
Thread 10, index 7, updated-item-2
Thread 9, index 6, value: updated-item-2
Thread 10, index 8, value: item-2
Thread 9, index 7, value: updated-item-2
Item swapped: true
Thread 9, index 8, value: updated-item-2
Thread 10, index 8, updated-item-2
Thread 9, index 9, value: item-2
Thread 10, index 9, value: item-10
Item swapped: false

Eine java.util.concurrent.Executor-Schnittstelle ist eine einfache Schnittstelle zur Unterstützung des Startens neuer Aufgaben.

ExecutorService-Methoden

Sr.Nr. Methode & Beschreibung
1

void execute(Runnable command)

Führt den angegebenen Befehl zu einem späteren Zeitpunkt aus.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der Executor-Schnittstelle in einer threadbasierten Umgebung.

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      Executor executor = Executors.newCachedThreadPool();
      executor.execute(new Task());
      ThreadPoolExecutor pool = (ThreadPoolExecutor)executor;
      pool.shutdown();
   }  

   static class Task implements Runnable {
      
      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed");
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Running Task!
Task Completed

Eine java.util.concurrent.ExecutorService-Schnittstelle ist eine Unterschnittstelle der Executor-Schnittstelle und fügt Funktionen zum Verwalten des Lebenszyklus sowohl der einzelnen Aufgaben als auch des Executors selbst hinzu.

ExecutorService-Methoden

Sr.Nr. Methode & Beschreibung
1

boolean awaitTermination(long timeout, TimeUnit unit)

Blockiert, bis alle Aufgaben nach einer Anforderung zum Herunterfahren ausgeführt wurden oder das Zeitlimit auftritt oder der aktuelle Thread unterbrochen wird, je nachdem, was zuerst eintritt.

2

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)

Führt die angegebenen Aufgaben aus und gibt eine Liste der Futures zurück, die ihren Status und ihre Ergebnisse enthalten, wenn alle abgeschlossen sind.

3

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

Führt die angegebenen Aufgaben aus und gibt eine Liste der Futures zurück, die ihren Status und ihre Ergebnisse enthalten, wenn alle abgeschlossen sind oder das Timeout abläuft, je nachdem, was zuerst eintritt.

4

<T> T invokeAny(Collection<? extends Callable<T>> tasks)

Führt die angegebenen Aufgaben aus und gibt gegebenenfalls das Ergebnis einer erfolgreich abgeschlossenen Aufgabe zurück (dh ohne eine Ausnahme auszulösen).

5

<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

Führt die angegebenen Aufgaben aus und gibt das Ergebnis einer Aufgabe zurück, die erfolgreich abgeschlossen wurde (dh ohne eine Ausnahme auszulösen), falls dies vor Ablauf des angegebenen Zeitlimits der Fall ist.
6

boolean isShutdown()

Gibt true zurück, wenn dieser Executor heruntergefahren wurde.

7

boolean isTerminated()

Gibt true zurück, wenn alle Aufgaben nach dem Herunterfahren abgeschlossen wurden.

8

void shutdown()

Leitet ein ordnungsgemäßes Herunterfahren ein, bei dem zuvor übermittelte Aufgaben ausgeführt werden, aber keine neuen Aufgaben akzeptiert werden.

9

List<Runnable> shutdownNow()

Es wird versucht, alle aktiv ausgeführten Aufgaben zu stoppen, die Verarbeitung wartender Aufgaben anzuhalten und eine Liste der Aufgaben zurückzugeben, die auf die Ausführung gewartet haben.

10

<T> Future<T> submit(Callable<T> task)

Sendet eine wertrückgebende Aufgabe zur Ausführung und gibt eine Zukunft zurück, die die ausstehenden Ergebnisse der Aufgabe darstellt.

11

Future<?> submit(Runnable task)

Sendet eine ausführbare Aufgabe zur Ausführung und gibt eine Zukunft zurück, die diese Aufgabe darstellt.

12

<T> Future<T> submit(Runnable task, T result)

Sendet eine ausführbare Aufgabe zur Ausführung und gibt eine Zukunft zurück, die diese Aufgabe darstellt.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der ExecutorService-Schnittstelle in einer threadbasierten Umgebung.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newSingleThreadExecutor();

      try {
         executor.submit(new Task());
         System.out.println("Shutdown executor");
         executor.shutdown();
         executor.awaitTermination(5, TimeUnit.SECONDS);
      } catch (InterruptedException e) {
         System.err.println("tasks interrupted");
      } finally {

         if (!executor.isTerminated()) {
            System.err.println("cancel non-finished tasks");
         }
         executor.shutdownNow();
         System.out.println("shutdown finished");
      }
   }

   static class Task implements Runnable {
      
      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 20);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }	   
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:302)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
	at TestThread$Task.run(TestThread.java:39)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

Eine java.util.concurrent.ScheduledExecutorService-Schnittstelle ist eine Unterschnittstelle der ExecutorService-Schnittstelle und unterstützt die zukünftige und / oder regelmäßige Ausführung von Aufgaben.

ScheduledExecutorService-Methoden

Sr.Nr. Methode & Beschreibung
1

<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

Erstellt eine ScheduledFuture und führt sie aus, die nach der angegebenen Verzögerung aktiviert wird.

2

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

Erstellt eine einmalige Aktion und führt sie aus, die nach der angegebenen Verzögerung aktiviert wird.

3

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

Erstellt eine periodische Aktion und führt sie aus, die zuerst nach der angegebenen anfänglichen Verzögerung und anschließend mit der angegebenen Periode aktiviert wird. Das heißt, die Ausführung beginnt nach initialDelay, dann initialDelay + period, dann initialDelay + 2 * period und so weiter.

4

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

Erstellt eine periodische Aktion und führt sie aus, die zuerst nach der angegebenen anfänglichen Verzögerung und anschließend mit der angegebenen Verzögerung zwischen der Beendigung einer Ausführung und dem Beginn der nächsten aktiviert wird.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der ScheduledExecutorService-Schnittstelle in einer threadbasierten Umgebung.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }

   static class BeepTask implements Runnable {
      
      public void run() {
         System.out.println("beep");      
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

beep
beep
beep
beep

Ein fester Thread-Pool kann durch Aufrufen der statischen newFixedThreadPool () -Methode der Executors-Klasse ermittelt werden.

Syntax

ExecutorService fixedPool = Executors.newFixedThreadPool(2);

wo

  • Maximal 2 Threads sind aktiv, um Aufgaben zu verarbeiten.

  • Wenn mehr als 2 Threads gesendet werden, werden sie in einer Warteschlange gehalten, bis Threads verfügbar werden.

  • Ein neuer Thread wird erstellt, um seinen Platz einzunehmen, wenn ein Thread aufgrund eines Fehlers beim Herunterfahren der Ausführung auf dem Executor noch nicht aufgerufen wird.

  • Jeder Thread ist vorhanden, bis der Pool heruntergefahren wird.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der newFixedThreadPool-Methode in einer threadbasierten Umgebung.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newFixedThreadPool(2);

      // Cast the object to its class type
      ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + pool.getCorePoolSize());
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
               TimeUnit.SECONDS.sleep(duration);
            
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Largest executions: 0
Maximum allowed threads: 2
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 2
Largest executions: 2
Maximum allowed threads: 2
Current threads in pool: 2
Currently executing threads: 1
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1

Ein zwischengespeicherter Thread-Pool kann durch Aufrufen der statischen newCachedThreadPool () -Methode der Executors-Klasse ermittelt werden.

Syntax

ExecutorService executor = Executors.newCachedThreadPool();

wo

  • Die newCachedThreadPool-Methode erstellt einen Executor mit einem erweiterbaren Thread-Pool.

  • Ein solcher Executor eignet sich für Anwendungen, die viele kurzlebige Aufgaben starten.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der newCachedThreadPool-Methode in einer threadbasierten Umgebung.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newCachedThreadPool();

      // Cast the object to its class type
      ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + pool.getCorePoolSize());
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
               TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1

Ein geplanter Thread-Pool kann durch Aufrufen der statischen newScheduledThreadPool () -Methode der Executors-Klasse ermittelt werden.

Syntax

ExecutorService executor = Executors.newScheduledThreadPool(1);

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der newScheduledThreadPool-Methode in einer threadbasierten Umgebung.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }  

   static class BeepTask implements Runnable {

      public void run() {
         System.out.println("beep");      
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

beep
beep
beep
beep

Ein einzelner Thread-Pool kann durch Aufrufen der statischen newSingleThreadExecutor () -Methode der Executors-Klasse ermittelt werden.

Syntax

ExecutorService executor = Executors.newSingleThreadExecutor();

Wobei die newSingleThreadExecutor-Methode einen Executor erstellt, der jeweils eine einzelne Aufgabe ausführt.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der newSingleThreadExecutor-Methode in einer threadbasierten Umgebung.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newSingleThreadExecutor();

      try {
         executor.submit(new Task());
         System.out.println("Shutdown executor");
         executor.shutdown();
         executor.awaitTermination(5, TimeUnit.SECONDS);
      } catch (InterruptedException e) {
         System.err.println("tasks interrupted");
      } finally {

         if (!executor.isTerminated()) {
            System.err.println("cancel non-finished tasks");
         }
         executor.shutdownNow();
         System.out.println("shutdown finished");
      }
   }

   static class Task implements Runnable {
      
      public void run() {

         try {
            Long duration = (long) (Math.random() * 20);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:302)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
	at TestThread$Task.run(TestThread.java:39) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

java.util.concurrent.ThreadPoolExecutor ist ein ExecutorService zum Ausführen jeder übermittelten Aufgabe mit einem von möglicherweise mehreren gepoolten Threads, die normalerweise mit den Factory-Methoden von Executors konfiguriert werden. Es bietet auch verschiedene Dienstprogrammmethoden, um aktuelle Thread-Statistiken zu überprüfen und zu steuern.

ThreadPoolExecutor-Methoden

Sr.Nr. Methode & Beschreibung
1

protected void afterExecute(Runnable r, Throwable t)

Methode, die nach Abschluss der Ausführung des angegebenen Runnable aufgerufen wird.

2

void allowCoreThreadTimeOut(boolean value)

Legt die Richtlinie fest, die regelt, ob Core-Threads eine Zeitüberschreitung aufweisen und beendet werden können, wenn innerhalb der Keep-Alive-Zeit keine Aufgaben eintreffen. Diese werden bei Bedarf ersetzt, wenn neue Aufgaben eintreffen.

3

boolean allowsCoreThreadTimeOut()

Gibt true zurück, wenn dieser Pool das Timeout von Core-Threads ermöglicht und beendet wird, wenn innerhalb der keepAlive-Zeit keine Aufgaben eintreffen, und wird bei Bedarf ersetzt, wenn neue Aufgaben eintreffen.

4

boolean awaitTermination(long timeout, TimeUnit unit)

Blockiert, bis alle Aufgaben nach einer Anforderung zum Herunterfahren ausgeführt wurden oder das Zeitlimit auftritt oder der aktuelle Thread unterbrochen wird, je nachdem, was zuerst eintritt.

5

protected void beforeExecute(Thread t, Runnable r)

Methode, die vor dem Ausführen der angegebenen Runnable im angegebenen Thread aufgerufen wird.

6

void execute(Runnable command)

Führt die angegebene Aufgabe irgendwann in der Zukunft aus.

7

protected void finalize()

Ruft das Herunterfahren auf, wenn auf diesen Executor nicht mehr verwiesen wird und keine Threads vorhanden sind.

8

int getActiveCount()

Gibt die ungefähre Anzahl von Threads zurück, die aktiv Aufgaben ausführen.

9

long getCompletedTaskCount()

Gibt die ungefähre Gesamtzahl der Aufgaben zurück, die die Ausführung abgeschlossen haben.

10

int getCorePoolSize()

Gibt die Kernanzahl der Threads zurück.

11

long getKeepAliveTime(TimeUnit unit)

Gibt die Keep-Alive-Zeit des Threads zurück. Dies ist die Zeit, die Threads, die die Größe des Kernpools überschreiten, möglicherweise inaktiv bleiben, bevor sie beendet werden.

12

int getLargestPoolSize()

Gibt die größte Anzahl von Threads zurück, die jemals gleichzeitig im Pool waren.

13

int getMaximumPoolSize()

Gibt die maximal zulässige Anzahl von Threads zurück.

14

int getPoolSize()

Gibt die aktuelle Anzahl der Threads im Pool zurück.

15

BlockingQueue getQueue()

Gibt die von diesem Executor verwendete Taskwarteschlange zurück.

15

RejectedExecutionHandler getRejectedExecutionHandler()

Gibt den aktuellen Handler für nicht ausführbare Aufgaben zurück.

16

long getTaskCount()

Gibt die ungefähre Gesamtzahl der Aufgaben zurück, deren Ausführung jemals geplant wurde.

17

ThreadFactory getThreadFactory()

Gibt die Thread-Factory zurück, die zum Erstellen neuer Threads verwendet wurde.

18

boolean isShutdown()

Gibt true zurück, wenn dieser Executor heruntergefahren wurde.

19

boolean isTerminated()

Gibt true zurück, wenn alle Aufgaben nach dem Herunterfahren abgeschlossen wurden.

20

boolean isTerminating()

Gibt true zurück, wenn dieser Executor gerade nach shutdown () oder shutdownNow () beendet wird, aber noch nicht vollständig beendet wurde.

21

int prestartAllCoreThreads()

Startet alle Kernthreads, sodass sie untätig auf die Arbeit warten.

22

boolean prestartCoreThread()

Startet einen Kernthread, wodurch er untätig auf die Arbeit wartet.

23

void purge()

Versucht, alle abgebrochenen zukünftigen Aufgaben aus der Arbeitswarteschlange zu entfernen.

24

boolean remove(Runnable task)

Entfernt diese Aufgabe aus der internen Warteschlange des Executors, wenn sie vorhanden ist. Dadurch wird sie nicht ausgeführt, wenn sie noch nicht gestartet wurde.

25

void setCorePoolSize(int corePoolSize)

Legt die Kernanzahl der Threads fest.

26

void setKeepAliveTime(long time, TimeUnit unit)

Legt das Zeitlimit fest, für das Threads möglicherweise inaktiv bleiben, bevor sie beendet werden.

27

void setMaximumPoolSize(int maximumPoolSize)

Legt die maximal zulässige Anzahl von Threads fest.

28

void setRejectedExecutionHandler(RejectedExecutionHandler handler)

Legt einen neuen Handler für nicht ausführbare Aufgaben fest.

29

void setThreadFactory(ThreadFactory threadFactory)

Legt die Thread-Factory fest, die zum Erstellen neuer Threads verwendet wird.

30

void shutdown()

Leitet ein ordnungsgemäßes Herunterfahren ein, bei dem zuvor übermittelte Aufgaben ausgeführt werden, aber keine neuen Aufgaben akzeptiert werden.

31

List<Runnable> shutdownNow()

Es wird versucht, alle aktiv ausgeführten Aufgaben zu stoppen, die Verarbeitung wartender Aufgaben anzuhalten und eine Liste der Aufgaben zurückzugeben, die auf die Ausführung gewartet haben.

32

protected void terminated()

Methode, die aufgerufen wird, wenn der Executor beendet wurde.

33

String toString()

Gibt eine Zeichenfolge zurück, die diesen Pool sowie seinen Status identifiziert, einschließlich Angaben zum Ausführungsstatus und zur geschätzten Anzahl von Arbeitern und Aufgaben.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der ThreadPoolExecutor-Schnittstelle in einer threadbasierten Umgebung.

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newCachedThreadPool();

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + executor.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + executor.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + executor.getPoolSize());
      System.out.println("Currently executing threads: "
         + executor.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + executor.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + executor.getCorePoolSize());
      System.out.println("Largest executions: "
         + executor.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + executor.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + executor.getPoolSize());
      System.out.println("Currently executing threads: "
         + executor.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + executor.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {

         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-2
Running Task! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-2

java.util.concurrent.ScheduledThreadPoolExecutor ist eine Unterklasse von ThreadPoolExecutor und kann zusätzlich Befehle planen, die nach einer bestimmten Verzögerung ausgeführt oder regelmäßig ausgeführt werden.

ScheduledThreadPoolExecutor-Methoden

Sr.Nr. Methode & Beschreibung
1

protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task)

Ändert oder ersetzt die Aufgabe, die zum Ausführen eines aufrufbaren Objekts verwendet wird.

2

protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task)

Ändert oder ersetzt die Aufgabe, die zum Ausführen einer ausführbaren Datei verwendet wird.

3

void execute(Runnable command)

Führt den Befehl ohne Verzögerung aus.

4

boolean getContinueExistingPeriodicTasksAfterShutdownPolicy()

Ruft die Richtlinie ab, ob vorhandene periodische Aufgaben auch dann ausgeführt werden sollen, wenn dieser Executor heruntergefahren wurde.

5

boolean getExecuteExistingDelayedTasksAfterShutdownPolicy()

Ruft die Richtlinie ab, ob vorhandene verzögerte Aufgaben ausgeführt werden sollen, auch wenn dieser Executor heruntergefahren wurde.

6

BlockingQueue<Runnable> getQueue()

Gibt die von diesem Executor verwendete Taskwarteschlange zurück.

7

boolean getRemoveOnCancelPolicy()

Ruft die Richtlinie ab, ob abgebrochene Aufgaben zum Zeitpunkt der Stornierung sofort aus der Arbeitswarteschlange entfernt werden sollen.

8

<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

Erstellt eine ScheduledFuture und führt sie aus, die nach der angegebenen Verzögerung aktiviert wird.

9

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

Erstellt eine einmalige Aktion und führt sie aus, die nach der angegebenen Verzögerung aktiviert wird.

10

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

Erstellt eine periodische Aktion und führt sie aus, die zuerst nach der angegebenen anfänglichen Verzögerung und anschließend mit der angegebenen Periode aktiviert wird. Das heißt, die Ausführung beginnt nach initialDelay, dann initialDelay + period, dann initialDelay + 2 * period und so weiter.

11

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

Erstellt eine periodische Aktion und führt sie aus, die zuerst nach der angegebenen anfänglichen Verzögerung und anschließend mit der angegebenen Verzögerung zwischen der Beendigung einer Ausführung und dem Beginn der nächsten aktiviert wird.

12

void setContinueExistingPeriodicTasksAfterShutdownPolicy (boolean value)

Legt die Richtlinie fest, ob vorhandene periodische Aufgaben auch dann ausgeführt werden sollen, wenn dieser Executor heruntergefahren wurde.

13

void setExecuteExistingDelayedTasksAfterShutdownPolicy (boolean value)

Legt die Richtlinie fest, ob vorhandene verzögerte Aufgaben ausgeführt werden sollen, auch wenn dieser Executor heruntergefahren wurde.

14

void setRemoveOnCancelPolicy(boolean value)

Legt die Richtlinie fest, ob abgebrochene Aufgaben zum Zeitpunkt der Stornierung sofort aus der Arbeitswarteschlange entfernt werden sollen.

15

void shutdown()

Leitet ein ordnungsgemäßes Herunterfahren ein, bei dem zuvor übermittelte Aufgaben ausgeführt werden, aber keine neuen Aufgaben akzeptiert werden.

16

List<Runnable> shutdownNow()

Es wird versucht, alle aktiv ausgeführten Aufgaben zu stoppen, die Verarbeitung wartender Aufgaben anzuhalten und eine Liste der Aufgaben zurückzugeben, die auf die Ausführung gewartet haben.

17

<T> Future<T> submit(Callable<T> task)

Sendet eine wertrückgebende Aufgabe zur Ausführung und gibt eine Zukunft zurück, die die ausstehenden Ergebnisse der Aufgabe darstellt.

18

Future<?> submit(Runnable task)

Sendet eine ausführbare Aufgabe zur Ausführung und gibt eine Zukunft zurück, die diese Aufgabe darstellt.

19

<T> Future<T> submit(Runnable task, T result)

Sendet eine ausführbare Aufgabe zur Ausführung und gibt eine Zukunft zurück, die diese Aufgabe darstellt.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der ScheduledThreadPoolExecutor-Schnittstelle in einer threadbasierten Umgebung.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledThreadPoolExecutor scheduler = 
         (ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }  

   static class BeepTask implements Runnable {
      
      public void run() {
         System.out.println("beep");      
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

beep
beep
beep
beep

Das Objekt java.util.concurrent.Callable kann das berechnete Ergebnis eines Threads zurückgeben, im Gegensatz zu einer ausführbaren Schnittstelle, die nur den Thread ausführen kann. Das Callable-Objekt gibt das Future-Objekt zurück, das Methoden zum Überwachen des Fortschritts einer von einem Thread ausgeführten Aufgabe bereitstellt. Zukünftiges Objekt kann verwendet werden, um den Status eines Callable zu überprüfen und das Ergebnis aus dem Callable abzurufen, sobald der Thread fertig ist. Es bietet auch Timeout-Funktionen.

Syntax

//submit the callable using ThreadExecutor
//and get the result as a Future object
Future<Long> result10 = executor.submit(new FactorialService(10));
 
//get the result using get method of the Future object
//get method waits till the thread execution and then return the result of the execution.
Long factorial10 = result10.get();

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung von Futures und Callables in einer threadbasierten Umgebung.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException,
      ExecutionException {

      ExecutorService executor = Executors.newSingleThreadExecutor();

      System.out.println("Factorial Service called for 10!");
      Future<Long> result10 = executor.submit(new FactorialService(10));

      System.out.println("Factorial Service called for 20!");
      Future<Long> result20 = executor.submit(new FactorialService(20));

      Long factorial10 = result10.get();
      System.out.println("10! = " + factorial10);

      Long factorial20 = result20.get();
      System.out.println("20! = " + factorial20);

      executor.shutdown();
   }  

   static class FactorialService implements Callable<Long> {
      private int number;

      public FactorialService(int number) {
         this.number = number;
      }

      @Override
      public Long call() throws Exception {
         return factorial();
      }

      private Long factorial() throws InterruptedException {
         long result = 1; 
         
         while (number != 0) { 
            result = number * result; 
            number--; 
            Thread.sleep(100); 
         }
         return result;	
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Factorial Service called for 10!
Factorial Service called for 20!
10! = 3628800
20! = 2432902008176640000

Das Fork-Join-Framework ermöglicht es, eine bestimmte Aufgabe für mehrere Worker zu unterbrechen und dann auf das Ergebnis zu warten, um sie zu kombinieren. Es nutzt die Kapazität der Multiprozessor-Maschine in hohem Maße. Im Folgenden sind die Kernkonzepte und -objekte aufgeführt, die im Fork-Join-Framework verwendet werden.

Gabel

Fork ist ein Prozess, bei dem sich eine Aufgabe in kleinere und unabhängige Unteraufgaben aufteilt, die gleichzeitig ausgeführt werden können.

Syntax

Sum left  = new Sum(array, low, mid);
left.fork();

Hier ist Sum eine Unterklasse von RecursiveTask und left.fork () fasst die Aufgabe in Unteraufgaben zusammen.

Beitreten

Join ist ein Prozess, bei dem eine Aufgabe alle Ergebnisse von Unteraufgaben zusammenfügt, sobald die Ausführung der Unteraufgaben abgeschlossen ist. Andernfalls wartet sie weiter.

Syntax

left.join();

Hier links ist ein Objekt der Summenklasse.

ForkJoinPool

Es handelt sich um einen speziellen Thread-Pool, der für die Aufteilung von Fork-and-Join-Aufgaben entwickelt wurde.

Syntax

ForkJoinPool forkJoinPool = new ForkJoinPool(4);

Hier ein neuer ForkJoinPool mit einer Parallelitätsstufe von 4 CPUs.

Rekursive Aktion

RecursiveAction stellt eine Aufgabe dar, die keinen Wert zurückgibt.

Syntax

class Writer extends RecursiveAction {
   @Override
   protected void compute() { }
}

RecursiveTask

RecursiveTask repräsentiert eine Aufgabe, die einen Wert zurückgibt.

Syntax

class Sum extends RecursiveTask<Long> {
   @Override
   protected Long compute() { return null; }
}

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung des Fork-Join-Frameworks in einer threadbasierten Umgebung.

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException, 
      ExecutionException {
      
      int nThreads = Runtime.getRuntime().availableProcessors();
      System.out.println(nThreads);
      
      int[] numbers = new int[1000]; 

      for(int i = 0; i < numbers.length; i++) {
         numbers[i] = i;
      }

      ForkJoinPool forkJoinPool = new ForkJoinPool(nThreads);
      Long result = forkJoinPool.invoke(new Sum(numbers,0,numbers.length));
      System.out.println(result);
   }  

   static class Sum extends RecursiveTask<Long> {
      int low;
      int high;
      int[] array;

      Sum(int[] array, int low, int high) {
         this.array = array;
         this.low   = low;
         this.high  = high;
      }

      protected Long compute() {
         
         if(high - low <= 10) {
            long sum = 0;
            
            for(int i = low; i < high; ++i) 
               sum += array[i];
               return sum;
         } else {	    	
            int mid = low + (high - low) / 2;
            Sum left  = new Sum(array, low, mid);
            Sum right = new Sum(array, mid, high);
            left.fork();
            long rightResult = right.compute();
            long leftResult  = left.join();
            return leftResult + rightResult;
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

32
499500

Eine java.util.concurrent.BlockingQueue-Schnittstelle ist eine Unterschnittstelle der Queue-Schnittstelle und unterstützt zusätzlich Vorgänge wie das Warten, bis die Warteschlange nicht leer ist, bevor ein Element abgerufen wird, und das Warten, bis Speicherplatz in der Warteschlange verfügbar ist, bevor ein Element gespeichert wird .

BlockingQueue-Methoden

Sr.Nr. Methode & Beschreibung
1

boolean add(E e)

Fügt das angegebene Element in diese Warteschlange ein, wenn dies ohne Verletzung der Kapazitätsbeschränkungen sofort möglich ist. Bei Erfolg wird true zurückgegeben und eine IllegalStateException ausgelöst, wenn derzeit kein Speicherplatz verfügbar ist.

2

boolean contains(Object o)

Gibt true zurück, wenn diese Warteschlange das angegebene Element enthält.

3

int drainTo(Collection<? super E> c)

Entfernt alle verfügbaren Elemente aus dieser Warteschlange und fügt sie der angegebenen Sammlung hinzu.

4

int drainTo(Collection<? super E> c, int maxElements)

Entfernt höchstens die angegebene Anzahl verfügbarer Elemente aus dieser Warteschlange und fügt sie der angegebenen Sammlung hinzu.

5

boolean offer(E e)

Fügt das angegebene Element in diese Warteschlange ein, wenn dies ohne Verletzung der Kapazitätsbeschränkungen sofort möglich ist, und gibt bei Erfolg true und false zurück, wenn derzeit kein Speicherplatz verfügbar ist.

6

boolean offer(E e, long timeout, TimeUnit unit)

Fügt das angegebene Element in diese Warteschlange ein und wartet bei Bedarf bis zur angegebenen Wartezeit, bis Speicherplatz verfügbar ist.

7

E poll(long timeout, TimeUnit unit)

Ruft den Kopf dieser Warteschlange ab und entfernt ihn. Warten Sie bei Bedarf bis zur angegebenen Wartezeit, bis ein Element verfügbar ist.

8

void put(E e)

Fügt das angegebene Element in diese Warteschlange ein und wartet bei Bedarf, bis Speicherplatz verfügbar ist.

9

int remainingCapacity()

Gibt die Anzahl der zusätzlichen Elemente zurück, die diese Warteschlange idealerweise (ohne Speicher- oder Ressourcenbeschränkungen) ohne Blockierung akzeptieren kann, oder Integer.MAX_VALUE, wenn es keine intrinsische Begrenzung gibt.

10

boolean remove(Object o)

Entfernt eine einzelne Instanz des angegebenen Elements aus dieser Warteschlange, falls vorhanden.

11

E take()

Ruft den Kopf dieser Warteschlange ab und entfernt ihn. Warten Sie gegebenenfalls, bis ein Element verfügbar ist.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung der BlockingQueue-Schnittstelle in einer threadbasierten Umgebung.

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);

      Producer producer = new Producer(queue);
      Consumer consumer = new Consumer(queue);

      new Thread(producer).start();
      new Thread(consumer).start();

      Thread.sleep(4000);
   }  


   static class Producer implements Runnable {
      private BlockingQueue<Integer> queue;

      public Producer(BlockingQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         Random random = new Random();

         try {
            int result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
            
            result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
            
            result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }	   
   }

   static class Consumer implements Runnable {
      private BlockingQueue<Integer> queue;

      public Consumer(BlockingQueue queue) {
         this.queue = queue;
      }
      
      @Override
      public void run() {
         
         try {
            System.out.println("Removed: " + queue.take());
            System.out.println("Removed: " + queue.take());
            System.out.println("Removed: " + queue.take());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Added: 52
Removed: 52
Added: 70
Removed: 70
Added: 27
Removed: 27

Eine java.util.concurrent.ConcurrentMap-Schnittstelle ist eine Unterschnittstelle der Map-Schnittstelle und unterstützt atomare Operationen für zugrunde liegende Map-Variablen. Es verfügt über Methoden zum Abrufen und Festlegen, die wie das Lesen und Schreiben flüchtiger Variablen funktionieren. Das heißt, eine Menge hat eine Vorher-Beziehung zu einem nachfolgenden Abruf derselben Variablen. Diese Schnittstelle gewährleistet Gewindesicherheit und Atomarität.

ConcurrentMap-Methoden

Sr.Nr. Methode & Beschreibung
1

default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

Versuche, eine Zuordnung für den angegebenen Schlüssel und seinen aktuellen zugeordneten Wert zu berechnen (oder null, wenn keine aktuelle Zuordnung vorhanden ist).

2

default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)

Wenn der angegebene Schlüssel noch keinem Wert zugeordnet ist (oder null zugeordnet ist), wird versucht, seinen Wert mithilfe der angegebenen Zuordnungsfunktion zu berechnen und in diese Zuordnung einzugeben, sofern nicht null.

3

default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

Wenn der Wert für den angegebenen Schlüssel vorhanden und nicht null ist, wird versucht, eine neue Zuordnung anhand des Schlüssels und seines aktuellen zugeordneten Werts zu berechnen.

4

default void forEach(BiConsumer<? super K,? super V> action)

Führt die angegebene Aktion für jeden Eintrag in dieser Zuordnung aus, bis alle Einträge verarbeitet wurden oder die Aktion eine Ausnahme auslöst.

5

default V getOrDefault(Object key, V defaultValue)

Gibt den Wert zurück, dem der angegebene Schlüssel zugeordnet ist, oder defaultValue, wenn diese Zuordnung keine Zuordnung für den Schlüssel enthält.

6

default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)

Wenn der angegebene Schlüssel noch keinem Wert oder null zugeordnet ist, ordnet er ihn dem angegebenen Wert ungleich Null zu.

7

V putIfAbsent(K key, V value)

Wenn der angegebene Schlüssel noch keinem Wert zugeordnet ist, ordnen Sie ihn dem angegebenen Wert zu.

8

boolean remove(Object key, Object value)

Entfernt den Eintrag für einen Schlüssel nur, wenn er aktuell einem bestimmten Wert zugeordnet ist.

9

V replace(K key, V value)

Ersetzt den Eintrag für einen Schlüssel nur, wenn er derzeit einem bestimmten Wert zugeordnet ist.

10

boolean replace(K key, V oldValue, V newValue)

Ersetzt den Eintrag für einen Schlüssel nur, wenn er aktuell einem bestimmten Wert zugeordnet ist.

11

default void replaceAll(BiFunction<? super K,? super V,? extends V> function)

Ersetzt den Wert jedes Eintrags durch das Ergebnis des Aufrufs der angegebenen Funktion für diesen Eintrag, bis alle Einträge verarbeitet wurden oder die Funktion eine Ausnahme auslöst.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung von ConcurrentMap gegenüber HashMap.

import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TestThread {

   public static void main(final String[] arguments) {
      Map<String,String> map = new ConcurrentHashMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial ConcurrentHashMap: " + map);
      Iterator<String> iterator = map.keySet().iterator();

      try { 
         
         while(iterator.hasNext()) {
            String key = iterator.next();
            
            if(key.equals("3")) {
               map.put("4", "Four");
            }
         }
      } catch(ConcurrentModificationException cme) {
         cme.printStackTrace();
      }
      System.out.println("ConcurrentHashMap after modification: " + map);

      map = new HashMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial HashMap: " + map);
      iterator = map.keySet().iterator();

      try {
         
         while(iterator.hasNext()) {
            String key = iterator.next();
            
            if(key.equals("3")) {
               map.put("4", "Four");
            }
         }
         System.out.println("HashMap after modification: " + map);
      } catch(ConcurrentModificationException cme) {
         cme.printStackTrace();
      }
   }  
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
ConcurrentHashMap after modification: {1 = One, 2 = Two, 3 = Three, 4 = Four, 5 = Five, 6 = Six}
Initial HashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(Unknown Source)
	at java.util.HashMap$KeyIterator.next(Unknown Source)
	at TestThread.main(TestThread.java:48)

Eine java.util.concurrent.ConcurrentNavigableMap-Schnittstelle ist eine Unterschnittstelle der ConcurrentMap-Schnittstelle und unterstützt NavigableMap-Operationen, und zwar rekursiv für ihre navigierbaren Unterkarten und ungefähren Übereinstimmungen.

ConcurrentMap-Methoden

Sr.Nr. Methode & Beschreibung
1

NavigableSet<K> descendingKeySet()

Gibt eine NavigableSet-Ansicht der in dieser Karte enthaltenen Schlüssel in umgekehrter Reihenfolge zurück.

2

ConcurrentNavigableMap<K,V> descendingMap()

Gibt eine Ansicht in umgekehrter Reihenfolge der in dieser Karte enthaltenen Zuordnungen zurück.

3

ConcurrentNavigableMap<K,V> headMap(K toKey)

Gibt eine Ansicht des Teils dieser Karte zurück, dessen Schlüssel streng kleiner als toKey sind.

4

ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive)

Gibt eine Ansicht des Teils dieser Karte zurück, dessen Schlüssel kleiner als (oder gleich, wenn inklusive wahr ist) toKey sind.

5

NavigableSet<K> keySet()

Gibt eine NavigableSet-Ansicht der in dieser Karte enthaltenen Schlüssel zurück.

6

NavigableSet<K> navigableKeySet()

Gibt eine NavigableSet-Ansicht der in dieser Karte enthaltenen Schlüssel zurück.

7

ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)

Gibt eine Ansicht des Teils dieser Karte zurück, dessen Schlüssel von Schlüssel zu Schlüssel reichen.

8

ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey)

Gibt eine Ansicht des Teils dieser Karte zurück, dessen Schlüssel von Schlüssel einschließlich bis zu Schlüssel exklusiv reichen.

9

ConcurrentNavigableMap<K,V> tailMap(K fromKey)

Gibt eine Ansicht des Teils dieser Karte zurück, dessen Schlüssel größer oder gleich fromKey sind.

10

ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive)

Gibt eine Ansicht des Teils dieser Karte zurück, dessen Schlüssel größer als (oder gleich, wenn inklusive wahr ist) vonKey sind.

Beispiel

Das folgende TestThread-Programm zeigt die Verwendung von ConcurrentNavigableMap.

import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

public class TestThread {

   public static void main(final String[] arguments) {
      ConcurrentNavigableMap<String,String> map =
         new ConcurrentSkipListMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial ConcurrentHashMap: "+map);
      System.out.println("HeadMap(\"2\") of ConcurrentHashMap: "+map.headMap("2"));
      System.out.println("TailMap(\"2\") of ConcurrentHashMap: "+map.tailMap("2"));
      System.out.println(
         "SubMap(\"2\", \"4\") of ConcurrentHashMap: "+map.subMap("2","4"));
   }  
}

Dies führt zu folgendem Ergebnis.

Ausgabe

Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
HeadMap("2") of ConcurrentHashMap: {1 = One}
TailMap("2") of ConcurrentHashMap: {2 = Two, 3 = Three, 5 = Five, 6 = Six}
SubMap("2", "4") of ConcurrentHashMap: {2 = Two, 3 = Three}