1. Wann treten die Sicherheitsprobleme bei Faden auf?
In einem einzigen Thread gibt es keine Sicherheitsprobleme mit Threads, aber in der Programmierung mit mehreren Threads ist es möglich, gleichzeitig auf die gleiche Ressource zuzugreifen. Diese Ressource kann verschiedene Arten von Ressourcen sein: eine Variable, ein Objekt, eine Datei, eine Datenbanktabelle usw. Wenn mehrere Threads gleichzeitig auf dieselbe Ressource zugreifen, wird ein Problem vorliegen:
Da der von jedem Thread ausgeführte Prozess unkontrollierbar ist, ist es wahrscheinlich, dass das Endergebnis dem tatsächlichen Wunsch widerspricht oder direkt zu Programmfehlern führt.
Lassen Sie uns ein einfaches Beispiel geben:
Jetzt gibt es zwei Threads, die Daten aus dem Netzwerk getrennt lesen und sie dann in eine Datenbanktabelle einfügen, sodass doppelte Daten nicht eingefügt werden können.
Dann muss es zwei Operationen im Prozess des Einfügungsdatens geben:
1) Überprüfen Sie, ob die Daten in der Datenbank vorhanden sind.
2) Wenn es existiert, wird es nicht eingefügt; Wenn es nicht existiert, wird es in die Datenbank eingefügt.
Wenn die beiden Threads durch Thread-1 bzw. Thread-2 bzw. irgendwann Thread-1 und Thread-2 gelesen werden, lesen Sie beide Data x, dies kann vorkommen:
Thread-1 prüft, ob Data X in der Datenbank vorhanden ist, und Thread-2 prüft auch, ob Data X in der Datenbank vorhanden ist.
Infolgedessen ist das Ergebnis der Überprüfung der beiden Threads, dass Data X in der Datenbank nicht vorhanden ist, sodass beide Threads Data X in die Datenbanktabelle einfügen.
Dies ist ein Problem mit Thread -Sicherheit, dh wenn mehrere Threads gleichzeitig auf eine Ressource zugreifen, ist das Programmlaufergebnis nicht das Ergebnis, das Sie sehen möchten.
Hier heißt diese Ressource: Kritische Ressource (auch als gemeinsam genannte Ressource bezeichnet).
Dies heißt, wenn mehrere Threads auf kritische Ressourcen zugreifen (ein Objekt, Attribute in einem Objekt, eine Datei, eine Datenbank usw.) können Probleme mit Thread -Sicherheit auftreten.
Wenn jedoch mehrere Threads eine Methode ausführen, sind lokale Variablen innerhalb der Methode keine kritischen Ressourcen, da die Methode auf dem Stapel ausgeführt wird, während der Java-Stapel Thread-privat ist, sodass keine Probleme mit der Sicherheit von Threads vorliegen.
2. Wie kann ich Probleme mit der Sicherheit von Threads lösen?
Wie kann man im Allgemeinen im Allgemeinen Fadensicherheitsprobleme lösen?
Bei der Lösung von Gewindesicherheitsproblemen übernehmen alle Parallelitätsmodi die Lösung "serialisierter Zugriff auf kritische Ressourcen", dh gleichzeitig kann nur ein Thread auf kritische Ressourcen zugreifen, die auch als synchronen gegenseitig ausschließlichen Zugriff bezeichnet werden.
Im Allgemeinen wird vor dem Code, der auf die kritische Ressource zugreift, eine Sperre hinzugefügt. Nach dem Zugriff auf die kritische Ressource wird die Sperre freigegeben und andere Threads zugreifen weiter.
In Java werden zwei Möglichkeiten bereitgestellt, um den synchronen Mutex -Zugriff zu implementieren: synchronisiert und sperren.
In diesem Artikel wird hauptsächlich über die Verwendung von Synchronisierungen gesprochen, und die Verwendung von Sperre wird im nächsten Blog -Beitrag beschrieben.
Drei.Synchronisierte Synchronisationsmethode oder Synchronisationsblock
Bevor Sie die Verwendung von synchronisierten Schlüsselwörtern verstehen, schauen wir uns zunächst ein Konzept an: MUTEX -Sperren, wie der Name schon sagt: Sperren, die den Zweck des Zugriffs von Mutex erreichen können.
Um ein einfaches Beispiel zu geben: Wenn einem Mutex eine kritische Ressource hinzugefügt wird, können die anderen Threads nur warten, wenn ein Thread auf die kritische Ressource zugreift.
In Java verfügt jedes Objekt über eine Sperrmarke (Monitor), auch als Monitor bezeichnet. Wenn mehrere Threads gleichzeitig auf ein Objekt zugreifen, kann der Thread nur darauf zugreifen, wenn er die Sperre des Objekts erfasst.
In Java kann das synchronisierte Schlüsselwort verwendet werden, um eine Methode oder einen Codeblock zu markieren. Wenn ein Thread die synchronisierte Methode des Objekts aufruft oder auf den synchronisierten Codeblock zugreift, erhält der Thread die Sperre des Objekts. Andere Threads können vorerst nicht auf die Methode zugreifen. Nur wenn die Methode ausgeführt wird oder der Codeblock ausgeführt wird, wird der Thread die Sperre des Objekts veröffentlicht und andere Threads können die Methode oder den Codeblock ausführen.
Im Folgenden finden Sie einige einfache Beispiele, um die Verwendung synchronisierter Schlüsselwörter zu veranschaulichen:
1. Synchronisierte Methode
Im folgenden Code rufen zwei Threads das InsertData -Objekt auf, um Daten einzufügen:
public class test {public static void main (String [] args) {Final InsertData InsertData = new InsertData (); neuer Thread () {public void run () {InsertData.insert (Thread.CurrentThread ()); }; }.Start(); neuer Thread () {public void run () {InsertData.insert (Thread.CurrentThread ()); }; }.Start(); }} class insertData {private ArrayList <Geuner> ArrayList = new ArrayList <GanzEger> (); public void Insert (Thread Thread) {for (int i = 0; i <5; i ++) {System.out.println (Thread.getName ()+"Daten einfügen"+i); arrayList.add (i); }}} Zu diesem Zeitpunkt lautet das Ausgabeergebnis des Programms:
Dies zeigt, dass zwei Threads gleichzeitig die Einfügungsmethode ausführen.
Wenn das synchronisierte Schlüsselwort vor der Einfügungsmethode hinzugefügt wird, ist das Auslaufergebnis:
Klasse InsertData {private arrayList <GeNeger> ArrayList = new ArrayList <Ganzzahl> (); public synchronisierte void Insert (Thread Thread) {für (int i = 0; i <5; i ++) {System.out.println (Thread.getName ()+"Daten einfügen"+i); arrayList.add (i); }}}Aus der obigen Ausgabe werden die in Thread-1 eingefügten Daten erst nach Einfügen von Thread-0 durchgeführt. Dies zeigt, dass Thread-0 und Thread-1 die Einfügungsmethoden nacheinander ausführen.
Dies ist die synchronisierte Methode.
Es gibt jedoch einige Punkte zu beachten:
1) Wenn ein Thread auf die synchronisierte Methode eines Objekts zugreift, können andere Threads nicht auf die anderen synchronisierten Methoden des Objekts zugreifen. Dieser Grund ist sehr einfach, da ein Objekt nur ein Schloss hat. Wenn ein Faden die Sperre des Objekts erfasst, können andere Threads die Sperre des Objekts nicht erhalten, sodass sie nicht auf andere synchronisierte Methoden des Objekts zugreifen können.
2) Wenn ein Thread auf die synchronisierte Methode eines Objekts zugreift, können andere Threads auf die nicht synchronisierte Methode des Objekts zugreifen. Der Grund dafür ist sehr einfach. Der Zugriff auf nicht synchronisierte Methoden erfordert nicht das Schloss des Objekts. Wenn eine Methode nicht mit dem synchronisierten Schlüsselwort geändert wird, bedeutet dies, dass sie keine kritischen Ressourcen verwendet, und andere Threads können auf diese Methode zugreifen.
3) Wenn ein Thread A auf die synchronisierte Methode Fun1 von Object Object1 zugreifen muss und ein anderer Thread B auf die synchronisierte Methode Fun1 von Object Object2 zugreifen muss, auch wenn Object1 und Object2 das gleiche Typ sind), gibt es kein Problem mit Thread -Sicherheit, da sie auf verschiedene Objekte zugreifen, sodass es kein gegenseitiges Ausschlussproblem gibt.
2. Synchronisierter Codeblock
Synchronisierte Codeblöcke ähneln dem folgenden Formular:
synchronisiert (SyncObject) {
}
Wenn dieser Codeblock in einem Thread ausgeführt wird, erfasst der Thread die Sperre des Objektsynobjekts, sodass es anderen Threads unmöglich ist, gleichzeitig auf den Codeblock zuzugreifen.
SynObject kann dies sein, das das Schloss darstellt, das das aktuelle Objekt erfasst, oder es kann ein Attribut in der Klasse sein, das die Sperre darstellt, die das Attribut erfasst.
Beispielsweise kann die obige Einfügemethode in die folgenden zwei Formulare geändert werden:
Klasse InsertData {private arrayList <GeNeger> ArrayList = new ArrayList <Ganzzahl> (); public void Insert (Thread Thread) {synchronisiert (this) {for (int i = 0; i <100; i ++) {System.out.println (Thread.getName ()+"Daten einfügen"+i); arrayList.add (i); }}}} class InsertData {private arrayList <GeNeger> ArrayList = new ArrayList <GanzEger> (); private Object Object = new Object (); public void Insert (Thread Thread) {synchronisiert (Objekt) {für (int i = 0; i <100; i ++) {System.out.println (thread.getName ()+"Daten einfügen"+i); arrayList.add (i); }}}}Wie aus den oben genannten Erscheinen hervorgeht, sind synchronisierte Codeblöcke viel flexibler als synchronisierte Methoden. Da möglicherweise nur ein Teil des Codes in einer Methode synchronisiert werden muss, wirkt sich die Programmausführungseffizienz aus. Dieses Problem kann durch Verwendung synchronisierter Codeblöcke vermieden werden. Synchronisierte Codeblöcke können nur die Synchronisation synchronisieren.
Darüber hinaus verfügt jede Klasse über eine Sperre, mit der der gleichzeitige Zugriff auf statische Datenelemente gesteuert werden kann.
Und wenn ein Thread die nicht statische synchronisierte Methode eines Objekts ausführt und ein anderer Thread die statische synchronisierte Methode der Klasse ausführen muss, zu der das Objekt gehört, gibt es zu diesem Zeitpunkt keinen gegenseitigen Ausschluss, da der Zugriff auf die statische synchronisierte Methode ein Klassenschloss einnimmt, wobei der Zugriff auf die nicht statische synchronisierte Methode ein Objektsperr aufnimmt.
Sie werden verstehen, indem Sie sich den folgenden Code ansehen:
public class test {public static void main (String [] args) {Final InsertData InsertData = new InsertData (); neuer Thread () {@Override public void run () {InsertData.insert (); } }.Start(); neuer Thread () {@Override public void run () {InsertData.insert1 (); } }.Start(); }} class InsertData {public synchronisierte void Insert () {System.out.println ("Insert ausführen"); try {thread.sleep (5000); } catch (interruptedException e) {e.printstacktrace (); } System.out.println ("Insert1 ausführen"); } public synchronisierte statische void Insert1 () {System.out.println ("Insert1"); System.out.println ("execute insert1"); }} Ausführungsergebnisse;
Die Einfügungsmethode wird im ersten Thread ausgeführt, wodurch der zweite Thread die Methode Insert1 nicht blockiert.
Schauen wir uns an, was das synchronisierte Schlüsselwort tut. Lassen Sie uns seinen Bytecode dekompilieren. Der dekompilierte Bytecode des folgenden Code ist:
öffentliche Klasse InsertData {private Object Object = new Object (); public void Insert (Thread Thread) {synchronisiert (Objekt) {}} public synchronisierte void Insert1 (Thread Thread) {} public void Insert2 (Thread Thread) {}}Aus dem Bytecode, der durch Dekompeten erhalten wurde, ist ersichtlich, dass der synchronisierte Codeblock tatsächlich zwei Anweisungen enthält: Monitenter und Monitorexit. Wenn der MonitorEnter -Befehl ausgeführt wird, wird die Schlosszahl des Objekts um 1 erhöht, während bei der Ausführung der Monitorexit -Anweisung die Schlosszahl des Objekts um 1 reduziert wird. Tatsächlich ist dies dem PV -Betrieb im Betriebssystem sehr ähnlich. Der PV -Betrieb im Betriebssystem wird verwendet, um mehrere Threads zu steuern, um auf kritische Ressourcen zuzugreifen. Für die synchronisierte Methode erkennt der Thread in der Ausführung, ob die Method_Info -Struktur der Methode die Einstellung von ACC_SYNCHRONISCHE Flag hat, und erfasst dann automatisch die Sperre des Objekts, ruft die Methode auf und veröffentlicht schließlich die Sperre. Wenn eine Ausnahme auftritt, veröffentlicht der Faden automatisch das Schloss.
Eine Sache zu beachten: Bei synchronisierten Methoden oder synchronisierten Codeblöcken wird bei einer Ausnahme die vom aktuelle Thread besetzte Sperre automatisch freigegeben, sodass aufgrund der Ausnahme kein Deadlock vorhanden ist.
3. Einige andere bemerkenswerte Dinge über synchronisiert
1. Der Unterschied zwischen synchronisierter und statischer Synchronisierung
Synchronisierte sperrt die aktuelle Instanz der Klasse, um zu verhindern, dass andere Threads gleichzeitig auf alle synchronisierten Blöcke der Instanz der Klasse zugreifen. Beachten Sie, dass dies die "aktuelle Instanz der Klasse" ist, und es gibt keine solche Einschränkungen für zwei verschiedene Instanzen der Klasse. Dann steuert statische Synchronisierung den Zugriff auf alle Instanzen der Klasse. Statisch synchronisiert beschränkt Threads, um gleichzeitig auf alle Instanzen der Klasse in JVM zuzugreifen und schnell auf den entsprechenden Code zuzugreifen. Wenn in einer Methode oder einem Codeblock in einer Klasse synchronisiert ist, hat die Klasse nach dem Generieren einer Instanz dieser Klasse eine schnelle Überwachung und der gleichzeitige Zugriff auf die modifizierte Instanz -Synchronisierung wird schnell geschützt. Die statische Synchronisierung ist die Öffentlichkeit der Überwachung, was der Unterschied zwischen den beiden ist. Das heißt, synchronisiert entspricht dies.
Ein japanischer Autor, Jie Chenghaos "Java Multithreaded Design Muster" hat eine solche Kolumne:
pulbic class there () {public synchronisierte void isynca () {} public synchronisierte void isyncb () {} public static synchronisierte void csynca () {} öffentliche statische synchronisierte void csyncb () {}}}}}}}}}}}}}}}} Wenn dann zwei Fälle von A und B von etwas Klasse hinzugefügt werden, warum kann dann auf die folgende Gruppe von Methoden gleichzeitig von mehr als einem Thread zugegriffen werden? Axissynca () und X.ISSyncb ()
bxissynca () und y.issynca ()
cxcsynca () und y.csyncb ()
dxissynca () und etwas.csynca ()
Hier ist klar, dass es beurteilt werden kann:
A, sowohl Zugriff auf die synchronisierte Domäne derselben Instanz, sodass nicht gleichzeitig zugegriffen werden kann. B ist für verschiedene Fälle, sodass gleichzeitig zugegriffen werden kann. Da es statisch synchronisiert ist, werden verschiedene Instanzen weiterhin eingeschränkt, was etwas entspricht.
Was ist also mit D?, Auf die Antwort im Buch kann gleichzeitig zugegriffen werden. Der Grund für die Antwort ist, dass Synchronzied ist, dass sich die Instanzmethode und die Synchronzed -Klassenmethode von der Sperre unterscheiden.
Persönliche Analyse bedeutet, dass synchronisierte und statische Synchronisierung zwei Banden entspricht, von denen jede seine eigene Kontrolle hat, und es gibt keine Einschränkungen aufeinander und kann gleichzeitig zugegriffen werden. Es ist nicht klar, wie synchronziert im internen Java -Design implementiert ist.
Schlussfolgerung: A: Synchronisierte statische ist der Umfang einer bestimmten Klasse. Synchronisierte statische CSYNC {} verhindert, dass mehrere Threads gleichzeitig auf die synchronisierte statische Methode in dieser Klasse zugreifen. Es funktioniert an allen Objektinstanzen einer Klasse.
B: Synchronisiert ist der Umfang einer Instanz. Synchronisierte ISSYNC () {} verhindert, dass mehrere Threads in dieser Instanz gleichzeitig auf die synchronisierte Methode zugreifen.
2. Die Differenz zwischen synchronisierter Methode und synchronisiertem Code schnell
Es gibt keinen Unterschied zwischen synchronisierten Methoden () {} und synchronisierten (this) {}, aber synchronisierte Methoden () {} ist bequem für das Leseverständnis, während synchronisierte (dieses) {} Konflikte, die den Zugriffsbereiche genauer beschränken, genauer steuern und manchmal effizienter abschneiden können.
3. Synchronisiertes Schlüsselwort kann nicht vererbt werden