Dieser Artikel untersucht hauptsächlich den Antragsmustercode der Java -Gleichzeitleistung blockierende Bedingung.
Die Bedingung unterteilt die Objektmonitormethoden (warten, benachrichtigen und benachrichtigen) in völlig unterschiedliche Objekte, so dass jedes Objekt durch Kombination dieser Objekte mit einer beliebigen Sperrimplementierung mehrere Wartezeitsätze (Wartezeiten) bietet. Unter ihnen ersetzt die Sperre die Verwendung synchronisierter Methoden und Aussagen und der Zustand ersetzt die Verwendung von Objektmonitormethoden.
Da der Zustand zum Ersetzen von Warten, Benachrichtigungen und anderen Methoden verwendet werden kann, können wir den zuvor geschriebenen Code der Kommunikation zwischen den Threads vergleichen und das ursprüngliche Problem ansehen:
Es gibt zwei Themen. Der untergeordnete Thread wird zuerst 10 Mal ausgeführt, dann wird der Haupt -Thread 5 -mal ausgeführt und dann zum untergeordneten Thread umgestellt, und dann wird der Haupt -Thread 5 -mal ausgeführt ... diese Roundreise beträgt 50 Mal.
Ich habe warten und benachrichtigen, um es vorher zu implementieren, aber jetzt verwende ich die Bedingung, um ihn neu zu schreiben, der Code ist wie folgt:
öffentliche Klasse Conditioncommunication {public static void main (String [] args) {business = new Business (); neuer Thread (new Runnable () {// einen untergeordneten Thread @Override public void run () {für (int i = 1; i <= 50; i ++) {bussine.sub (I.); 50; oder false; condition.Signal (); // Verwenden Sie die Bedingung, um ein Wakeup-Signal zu senden und ein bestimmtes aufzuwecken. blocke.printstacktrace ();}} für (int j = 1; j <= 10; j ++) {System.out.println ("Haupt-Thread-Sequenz von" + j + ", Schleife von" + i);} bshouldsub = true;Aus Sicht der Code wird die Bedingung zusammen mit der Sperre verwendet. Ohne Schloss kann der Zustand nicht verwendet werden, da der Zustand durch Sperre erzeugt wird. Diese Verwendung ist sehr einfach. Solange Sie die Verwendung von synchronisiertem, warten und benachrichtigen, können Sie die Verwendung von Schloss und Zustand vollständig beherrschen.
Das obige verwendet Sperre und Zustand anstelle von synchronisierten und Objektmonitormethoden, um die Kommunikation zwischen zwei Threads zu realisieren. Schreiben wir nun eine etwas fortgeschrittenere Anwendung: simulieren wir die blockierende Warteschlange des Puffer.
Was ist ein Puffer? Zum Beispiel gibt es viele Personen, die jetzt Nachrichten senden möchten. Ich bin eine Transitstation und möchte anderen helfen, Nachrichten zu senden. Jetzt muss ich zwei Dinge tun. Eine Sache ist, Nachrichten zu empfangen, die vom Benutzer gesendet und in den Puffer in Ordnung gebracht werden. Die andere Sache ist, Nachrichten, die vom Benutzer gesendet werden, aus dem Puffer herauszunehmen und sie auszuschicken.
Jetzt abstrakt dieses tatsächliche Problem: Ein Puffer ist ein Array. Wir können Daten in das Array schreiben oder Daten aus dem Array herausnehmen. Die beiden Dinge, die ich tun muss, sind, zwei Threads zu starten, eine zum Speichern von Daten und die andere, um Daten zu erhalten. Das Problem ist jedoch, dass, wenn der Puffer voll ist, dies bedeutet, dass zu viele Nachrichten empfangen werden. Die gesendete Nachricht ist zu schnell, und ein weiterer Thread meines Lebens reicht nicht aus, um ihn zu senden, was dazu führt, dass der Puffer ausgelassen wird, sodass der Datenspeicher des Datenspeichers blockiert und warten muss. Im Gegenteil, wenn ich es zu schnell weiterleite und jetzt alle Inhalte des Puffers von mir gesendet wurden und der Benutzer keine neuen Nachrichten gesendet wurden, muss der Thread der Datenerfassung zu diesem Zeitpunkt blockiert werden.
OK, nutzen wir nach der Analyse der blockierenden Warteschlange dieses Puffers die Bedingungstechnologie, um sie zu implementieren:
Klassenpuffer {Final Lock Lock = new Reentrantlock (); // eine Schließbedingung nicht abschließen. Notfull = lock.Newcondition (); // Bedingung finales Zustand notingimpty = lock.newCondition (); // Definieren Sie das Bedingungsobjekt []. count; // Array -Indexs werden verwendet, um die Position // Speicherdaten in die Warteschlange zu kalibrieren. Sein! "); Notfull.aawait (); // Wenn die Warteschlange voll ist, blockieren Sie den Thread der Datenspeicherung und warten darauf, aufgeweckt zu werden Wenn es erreicht ist, kehren Sie zum Anfang PutPtr = 0; ++ Count; // Anzahl des Nachrichtensystems.out.println (Thread.currentThread (). GetName () + "Speichern Sie den Wert:" + x); Notimpty.Signal (); // Okay, jetzt gibt es Daten in der Warteschlange. Wachen Sie den Thread mit leerer Warteschlange auf und Sie können die Daten abrufen. Die Zeit sein! "); Notimpty.await (); // Wenn die Warteschlange leer ist, blockiert der Thread die Daten, um den Thread abzurufen und darauf zu warten, aufgeweckt zu werden} // Wenn er nicht leer ist, nehmen Sie das Objekt x = items [takeptr]; wenn (++ takeptr == items.Length) // beurteilen, ob das Ende ankommt. System.out.println (Thread.currentThread (). GetName () + "den Wert herausholen:" + x); Notfull.Signal (); // Okay, jetzt gibt es einen Ort in der Warteschlange. Wachen Sie den Thread voller Warteschlangen auf und Sie können Daten speichern. Return x;} schließlich {lock.unlock (); // lock}}}Dieses Programm ist klassisch, ich habe es aus der offiziellen JDK -Dokumentation herausgenommen und Kommentare hinzugefügt. Es gibt zwei Bedingungen im Programm, mit denen die beiden Threads ausgeführt werden und warten und aufwachen. Die Idee ist klar und das Programm ist auch sehr robust. Eine Frage kann berücksichtigt werden, warum Sie zwei Codes verwenden müssen? Es muss einen Grund für dieses Design geben. Wenn Sie eine Bedingung verwenden, gehen Sie nun davon aus, dass die Warteschlange voll ist, aber es gibt 2 Threads A und B, die gleichzeitig Daten speichern, und dann geben sie alle in den Schlaf ein. OK, jetzt nimmt ein weiterer Thread einen weg und weckt dann einen der Threads A, dann kann er ihn retten. Nach dem Speichern wacht ein weiterer Thread auf. Wenn B geweckt wird, wird es ein Problem geben, da die Warteschlange zu diesem Zeitpunkt voll ist und B es nicht speichern kann. Wenn B speichert, überschreiben Sie den Wert, der nicht abgerufen wurde. Da eine Erkrankung verwendet wird, verwenden sowohl Speicher als auch Entzug diese Erkrankung, um zu schlafen und aufzuwachen, und wird durcheinander gebracht. Zu diesem Zeitpunkt können Sie die Verwendung dieses Zustands verstehen. Testen wir nun den Effekt der obigen blockierenden Warteschlange:
public class bodedbuffer {public static void main (String [] args) {buffer buffer = new buffer (); für (int i = 0; i <5; i ++) {// 5 Threads öffnen, um Daten im Puffer neu zu speichern (New Thread New Thread (new Runnable) () {@override public void run () {try {try -puffer.put. (InterruptedException e) {e.printStacktrace ();}}}). Start ();} für (int i = 0; i <10; i ++) {// 10 Threads öffnen, um Daten aus dem Puffer New Thread (New Runnable () {@Override öffentlicher void Run () {try {) {buffer (thread (); {e.printstacktrace ();}}}). start ();}}}}Ich habe absichtlich nur 5 Threads aktiviert, um Daten und 10 Threads zu speichern, um Daten abzurufen, nur um sie vom Erhalt von Daten blockiert zu machen und die Ergebnisse des Vorgangs zu sehen:
Thread-5 ist blockiert und Daten können vorerst nicht abgerufen werden!
Thread-10 ist blockiert und Daten können vorerst nicht abgerufen werden!
Thread-1 gespeicherter Wert: 755
Thread-0 gespeicherter Wert: 206
Thread-2 gespeicherter Wert: 741
Thread-3 gespeicherter Wert: 381
Thread-14 entfernt Wert: 755
Thread-4 gespeicherter Wert: 783
Thread-6 Nehmen Sie den Wert heraus: 206
Thread-7 entfernt Wert: 741
Thread-8 entfernt Wert: 381
Thread-9 Nehmen Sie den Wert heraus: 783
Thread-5 ist blockiert und Daten können vorerst nicht abgerufen werden!
Thread-11 ist blockiert und Daten können vorerst nicht abgerufen werden!
Thread-12 ist blockiert und Daten können vorerst nicht abgerufen werden!
Thread-10 ist blockiert und Daten können vorerst nicht abgerufen werden!
Thread-13 ist blockiert und Daten können vorerst nicht abgerufen werden!
Aus den Ergebnissen können wir sehen, dass die Threads 5 und 10 zuerst ausgeführt werden und feststellen, dass es in der Warteschlange keine gibt, so dass sie blockiert sind und dort schlafen. Sie können es nur bekommen, bis ein neuer Wert in der Warteschlange gespeichert ist. Sie haben jedoch kein Glück, und die gespeicherten Daten werden zuerst von anderen Threads genommen. Haha ... sie können es noch ein paar Mal laufen lassen. Wenn Sie sehen möchten, dass die gespeicherten Daten blockiert sind, können Sie den Thread so einstellen, dass die Daten ein wenig weniger abrufen, und ich werde ihn hier nicht festlegen.
Es ist immer noch die gleiche Frage wie zuvor. Lassen Sie es nun drei Threads ausführen. Schauen wir uns die Frage an:
Es gibt drei Threads, untergeordnetes Thread 1 führt zuerst 10 Mal aus, untergeordnetes Thread 2 führt das 10 -fache aus, dann führt der Haupt -Thread 5 -mal aus und wechselt dann zum untergeordneten Thread.
Wenn Sie keinen Zustand verwenden, ist es wirklich schwierig, es zu tun, aber es ist sehr bequem, dies mit Zustand zu tun. Das Prinzip ist sehr einfach. Drei Bedingungen definieren. Nachdem Child Thread 1 ausgeführt wurde, weckt Child Thread 2 den Hauptfaden auf und der Hauptfaden weckt den Kinderfaden 1. Der Weckmechanismus ähnelt dem obigen Puffer. Schauen wir uns den folgenden Code an, es ist leicht zu verstehen.
öffentliche Klasse ThreeconditionCommunication {public static void main (String [] args) {Business business = new Business (); neuer Thread (new Runnable () {// einen untergeordneten Thread @Override public void run () {für (int i = 1; i <= 50; i ++) {) New Thread.Sub1 (i);}}}). {// Ein weiterer untergeordneter Thread @Override public void run () {für (int i = 1; i <= 50; i ++) {bussiness.sub2 (i);}}}). condition1 = lock.newcondition (); // Bedingung ist eine Bedingung2 = lock.NewCondition (); Bedingung ConditionMain = lock.newCondition (); private int bshouldSub = 0; Oder {lock.unlock ();}} public void sub2 (int i) {lock.lock (); try {while (bshouldSub! = 1) {try {Condition2.Await (); // Verwenden Sie die Bedingung, um die Warteverfahren zu nennen} catch (Ausnahme E) {// todo auto-generated blocKe.Printce. J ++) {System.out.println ("Sub2 -Threadsequenz von" + J + ", Schleife von" + i);} bsouldSub = 2; ConditionMain.Signal (); // Lassen Sie den Haupt -Thread endlich} schließlich {lock.unlock ();}} public void Main (int i) {{lock.Lock () (); try {probsub (bshouldsub (int i) {{) {lock.Lock () (); {conditionMain.await();//Use condition to call the await method}catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}for (int j = 1; j <= 5; j++) {System.out.println("main thread sequence of " + j + ", loop of " + i);}bShouldSub = 0; Condition1.Signal (); // Thread 1 ausführen} endlich {lock.unlock ();}}}}Der Code scheint ein bisschen lang zu sein, aber es ist eine Illusion und die Logik ist sehr einfach. Das ist alles, um die Zustandstechnologie in Threads zusammenzufassen.
Das obige ist der gesamte Inhalt dieses Artikels zum Beispiel für den Anwendungscode -Beispiel für die Blockierung des Blockierens von Java -Parallelität. Ich hoffe, es wird für alle hilfreich sein. Interessierte Freunde können weiterhin auf andere verwandte Themen auf dieser Website verweisen. Wenn es Mängel gibt, hinterlassen Sie bitte eine Nachricht, um darauf hinzuweisen. Vielen Dank an Freunde für Ihre Unterstützung für diese Seite!