Gemeinsame Daten sind eine der wichtigsten Funktionen gleichzeitiger Programme. Dies ist ein sehr wichtiger Aspekt, unabhängig davon, ob es sich um ein Objekt handelt, das die Thread-Klasse erbt, oder um ein Objekt, das die Runnable-Schnittstelle implementiert.
Wenn Sie ein Objekt einer Klasse erstellen, die die Runnable-Schnittstelle implementiert, und dieses Objekt verwenden, um eine Reihe von Threads zu starten, haben alle diese Threads dieselben Eigenschaften. Mit anderen Worten: Wenn ein Thread eine Eigenschaft ändert, sind alle verbleibenden Threads von der Änderung betroffen.
Manchmal ziehen wir es vor, es allein innerhalb eines Threads zu verwenden, anstatt es mit anderen Threads zu teilen, die mit demselben Objekt gestartet wurden. Die Java-Parallelitätsschnittstelle bietet einen sehr klaren Mechanismus zur Erfüllung dieser Anforderung, sogenannte Thread-lokale Variablen. Auch die Leistung dieses Mechanismus ist sehr beeindruckend.
weiß es
Befolgen Sie die unten aufgeführten Schritte, um das Beispielprogramm fertigzustellen.
1. Implementieren Sie zunächst ein Programm mit den oben genannten Problemen. Erstellen Sie eine Klasse mit dem Namen UnsafeTask und implementieren Sie die Runnable-Schnittstelle. Deklarieren Sie eine private Eigenschaft vom Typ java.util.Date in der Klasse. Der Code lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
Die öffentliche Klasse UnsafeTask implementiert Runnable {
privates Datum startDate;
2. Implementieren Sie die run()-Methode von UnsafeTask, die das startDate-Attribut instanziiert und seinen Wert an die Konsole ausgibt. Für einen zufälligen Zeitraum in den Ruhezustand versetzen und dann den Wert des startDate-Attributs erneut auf der Konsole ausgeben. Der Code lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
@Override
public void run() {
startDate = neues Datum();
System.out.printf("Thread wird gestartet: %s: %s/n",
Thread.currentThread().getId(), startDate);
versuchen {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} Catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Thread beendet: %s : %s/n",
Thread.currentThread().getId(), startDate);
}
3. Implementieren Sie die Hauptklasse des problematischen Programms. Erstellen Sie eine Klasse mit einer main()-Methode, UnsafeMain. Erstellen Sie in der main()-Methode ein UnsafeTask-Objekt und verwenden Sie dieses Objekt, um 10 Thread-Objekte zu erstellen, um 10 Threads zu starten. Schlafen Sie in der Mitte jedes Threads 2 Sekunden lang. Der Code lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
öffentliche Klasse UnsafeMain {
public static void main(String[] args) {
UnsafeTask task = new UnsafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = neuer Thread(Aufgabe);
thread.start();
versuchen {
TimeUnit.SECONDS.sleep(2);
} Catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
4. Aus der obigen Logik geht hervor, dass jeder Thread eine andere Startzeit hat. Dem Ausgabeprotokoll unten zufolge gibt es jedoch viele identische Zeitwerte. wie folgt:
Kopieren Sie den Codecode wie folgt:
Startthread: 9: So, 29. September, 23:31:08 CST 2013
Startthread: 10: So, 29. September, 23:31:10 Uhr CST 2013
Startthread: 11: So, 29. September, 23:31:12 CST 2013
Startthread: 12: So, 29. September, 23:31:14 Uhr CST 2013
Thread beendet: 9: So, 29. September, 23:31:14 Uhr CST 2013
Startthread: 13: So, 29. September, 23:31:16 Uhr CST 2013
Thread beendet: 10: So, 29. September, 23:31:16 Uhr CST 2013
Startthread: 14: So, 29. September, 23:31:18 Uhr CST 2013
Thread beendet: 11: So, 29. September, 23:31:18 Uhr CST 2013
Startthread: 15: So, 29. September, 23:31:20 Uhr CST 2013
Thread beendet: 12: So, 29. September 23:31:20 CST 2013
Startthread: 16: So, 29. September, 23:31:22 CST 2013
Startthread: 17: So, 29. September, 23:31:24 CST 2013
Thread beendet: 17: So, 29. September, 23:31:24 CST 2013
Thread beendet: 15: So, 29. September, 23:31:24 CST 2013
Thread beendet: 13: So, 29. September, 23:31:24 CST 2013
Startthread: 18: So, 29. September, 23:31:26 Uhr CST 2013
Thread beendet: 14: So, 29. September, 23:31:26 Uhr CST 2013
Thread beendet: 18: So, 29. September, 23:31:26 Uhr CST 2013
Thread beendet: 16: So, 29. September, 23:31:26 Uhr CST 2013
5. Wie oben gezeigt, werden wir den Thread-lokalen Variablenmechanismus verwenden, um dieses Problem zu lösen.
6. Erstellen Sie eine Klasse mit dem Namen SafeTask und implementieren Sie die Runnable-Schnittstelle. Der Code lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
Die öffentliche Klasse SafeTask implementiert Runnable {
7. Deklarieren Sie ein Objekt vom Typ ThreadLocal<Date>. Wenn das Objekt instanziiert wird, wird die Methode initialValue() überschrieben und der tatsächliche Datumswert in dieser Methode zurückgegeben. Der Code lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
privates statisches ThreadLocal<Datum> startDate = neu
ThreadLocal<Datum>() {
@Override
protected Date initialValue() {
return new Date();
}
};
8. Implementieren Sie die run()-Methode der SafeTask-Klasse. Diese Methode ist mit der run()-Methode von UnsafeTask identisch, außer dass die Methode des startDate-Attributs leicht angepasst ist. Der Code lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
@Override
public void run() {
System.out.printf("Thread wird gestartet: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
versuchen {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} Catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Thread beendet: %s : %s/n",
Thread.currentThread().getId(), startDate.get());
}
9. Die Hauptklasse dieses sicheren Beispiels ist im Wesentlichen dieselbe wie die Hauptklasse des nicht sicheren Programms, außer dass UnsafeTask in SafeTask geändert werden muss. Der spezifische Code lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
öffentliche Klasse SafeMain {
public static void main(String[] args) {
SafeTask-Aufgabe = new SafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = neuer Thread(Aufgabe);
thread.start();
versuchen {
TimeUnit.SECONDS.sleep(2);
} Catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
10. Führen Sie das Programm aus und analysieren Sie die Unterschiede zwischen den beiden Eingaben.
Um die Benennung von Klassen zu standardisieren, unterscheidet sich die Benennung der Hauptklasse in diesem Artikel geringfügig vom Originaltext. Darüber hinaus sind das ursprüngliche Programm und die Textbeschreibung inkonsistent. Es muss ein Schreibfehler sein.
weiß warum
Nachfolgend finden Sie das Ausführungsergebnis des Sicherheitsbeispiels. Anhand der Ergebnisse lässt sich leicht erkennen, dass jeder Thread über einen startDate-Attributwert verfügt, der zum jeweiligen Thread gehört. Die Programmeingabe ist wie folgt:
Kopieren Sie den Codecode wie folgt:
Startthread: 9: So, 29. September, 23:52:17 CST 2013
Startthread: 10: So, 29. September, 23:52:19 CST 2013
Startthread: 11: So, 29. September, 23:52:21 CST 2013
Thread beendet: 10: So, 29. September, 23:52:19 CST 2013
Startthread: 12: So, 29. September, 23:52:23 CST 2013
Thread beendet: 11: So, 29. September 23:52:21 CST 2013
Startthread: 13: So, 29. September, 23:52:25 CST 2013
Thread beendet: 9: So, 29. September, 23:52:17 Uhr CST 2013
Startthread: 14: So, 29. September, 23:52:27 CST 2013
Startthread: 15: So, 29. September, 23:52:29 CST 2013
Thread beendet: 13: So, 29. September, 23:52:25 CST 2013
Startthread: 16: So, 29. September, 23:52:31 CST 2013
Thread beendet: 14: So, 29. September, 23:52:27 CST 2013
Startthread: 17: So, 29. September, 23:52:33 CST 2013
Thread beendet: 12: So, 29. September, 23:52:23 CST 2013
Thread beendet: 16: So, 29. September, 23:52:31 Uhr CST 2013
Thread beendet: 15: So, 29. September, 23:52:29 CST 2013
Startthread: 18: So, 29. September, 23:52:35 CST 2013
Thread beendet: 17: So, 29. September, 23:52:33 CST 2013
Thread beendet: 18: So, 29. September, 23:52:35 CST 2013
Lokale Thread-Variablen speichern eine Kopie der Eigenschaft für jeden Thread. Sie können die get()-Methode von ThreadLocal verwenden, um den Wert einer Variablen abzurufen, und die set()-Methode verwenden, um den Wert einer Variablen festzulegen. Wenn zum ersten Mal auf eine Thread-lokale Variable zugegriffen wird und der Variablen noch kein Wert zugewiesen wurde, wird die Methode initialValue() aufgerufen, um für jeden Thread einen Wert zu initialisieren.
niemals enden
Die ThreadLocal-Klasse stellt außerdem die Methode „remove()“ bereit, um den lokalen Variablenwert zu löschen, der in dem Thread gespeichert ist, der diese Methode aufruft.
Darüber hinaus stellt die Java-Parallelitäts-API auch die InheritableThreadLocal-Klasse bereit, mit der der untergeordnete Thread die Anfangswerte aller vererbbaren lokalen Thread-Variablen empfangen kann, um den Wert zu erhalten, der dem übergeordneten Thread gehört. Wenn Thread A über eine lokale Thread-Variable verfügt und Thread A Thread B erstellt, verfügt Thread B über dieselbe lokale Thread-Variable wie Thread A. Sie können childValue() auch überschreiben, um die lokalen Thread-Variablen des untergeordneten Threads zu initialisieren. Diese Methode akzeptiert den Wert einer Thread-lokalen Variablen, die als Parameter vom übergeordneten Thread übergeben wird.
Nutzen Sie die Lehre
Dieser Artikel wurde aus dem „Java 7 Concurrency Cookbook“ übersetzt (D Gua Ge hat ihn als „Java7 Concurrency Beispielsammlung“ gestohlen) und dient nur als Lernmaterial. Es darf ohne Genehmigung nicht für kommerzielle Zwecke verwendet werden.
Kleiner Erfolg
Nachfolgend finden Sie eine vollständige Version des gesamten Codes, der in den Beispielen in diesem Abschnitt enthalten ist.
Vollständiger Code der UnsafeTask-Klasse:
Kopieren Sie den Codecode wie folgt:
Paket com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Beispiele, bei denen die Thread-Sicherheit nicht garantiert werden kann
* Datum: 23.09.2013
* Zeit: 23:58
*/
Die öffentliche Klasse UnsafeTask implementiert Runnable {
privates Datum startDate;
@Override
public void run() {
startDate = neues Datum();
System.out.printf("Thread wird gestartet: %s: %s/n",
Thread.currentThread().getId(), startDate);
versuchen {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} Catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Thread beendet: %s : %s/n",
Thread.currentThread().getId(), startDate);
}
}
Vollständiger Code der UnsafeMain-Klasse:
Kopieren Sie den Codecode wie folgt:
Paket com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.concurrent.TimeUnit;
/**
* Beispiel für einen unsicheren Thread
* Datum: 24.09.2013
* Zeit: 00:04
*/
öffentliche Klasse UnsafeMain {
public static void main(String[] args) {
UnsafeTask task = new UnsafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = neuer Thread(Aufgabe);
thread.start();
versuchen {
TimeUnit.SECONDS.sleep(2);
} Catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Vollständiger Code der SafeTask-Klasse:
Kopieren Sie den Codecode wie folgt:
Paket com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Verwenden Sie lokale Thread-Variablen, um die Thread-Sicherheit zu gewährleisten
* Datum: 29.09.2013
* Zeit: 23:34
*/
Die öffentliche Klasse SafeTask implementiert Runnable {
privates statisches ThreadLocal<Datum> startDate = neu
ThreadLocal<Datum>() {
@Override
protected Date initialValue() {
return new Date();
}
};
@Override
public void run() {
System.out.printf("Thread wird gestartet: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
versuchen {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} Catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Thread beendet: %s : %s/n",
Thread.currentThread().getId(), startDate.get());
}
}
Vollständiger Code der SafeMain-Klasse:
Kopieren Sie den Codecode wie folgt:
Paket com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.concurrent.TimeUnit;
/**
* Beispiel für einen sicheren Thread
* Datum: 24.09.2013
* Zeit: 00:04
*/
öffentliche Klasse SafeMain {
public static void main(String[] args) {
SafeTask-Aufgabe = new SafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = neuer Thread(Aufgabe);
thread.start();
versuchen {
TimeUnit.SECONDS.sleep(2);
} Catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}