Text
Bei der Programmierung in einer gleichzeitigen Umgebung ist ein Sperrmechanismus erforderlich, um den Vorgang zwischen mehreren Threads zu synchronisieren, um einen gegenseitig ausschließenden Zugriff auf gemeinsam genutzte Ressourcen zu gewährleisten. Durch Verriegelung kann es zu Leistungsschaden führen, was bekannt zu sein scheint. Das Sperren selbst bringt jedoch nicht viel Leistungsverbrauch mit sich, und die Leistung ist hauptsächlich der Prozess des Erwerbs von Schlössern in Threads. Wenn nur ein Thread um Schlösser konkurriert und es zu diesem Zeitpunkt keinen Wettbewerb mit mehreren Threads gibt, wird der JVM optimiert, und der durch Verriegelung verursachte Leistungsverbrauch kann grundsätzlich ignoriert werden. Daher kann die Standardisierung des Betriebs des Sperrens, der Optimierung der Nutzungsmethode für das Sperren und die Vermeidung unnötiger Gewindewettbewerbe nicht nur die Programmleistung verbessern, sondern auch die Möglichkeit einer durch unregelmäßigen Verriegelung verursachten Faden -Deadlocking vermeiden und die Robustheit der Programme verbessern. Im Folgenden werden mehrere Ideen für die Sperroptimierung erläutert.
1. Versuchen Sie, die Methode nicht zu sperren
Wenn eine normale Elementfunktion hinzugefügt wird, erhält der Thread die Objektsperrung des Objekts, in dem sich die Methode befindet. Zu diesem Zeitpunkt wird das gesamte Objekt gesperrt. Dies bedeutet auch, dass wenn die von diesem Objekt bereitgestellten Mehrfachsynchronisierungsmethoden für verschiedene Dienste bestimmt sind, da das gesamte Objekt gesperrt ist und ein Unternehmen verarbeitet wird, müssen auch andere nicht verwandte Geschäftsthreads warten. Das folgende Beispiel zeigt dies:
Die LockMethod -Klasse enthält zwei Synchronisationsmethoden, die in zwei Geschäftsprozessen aufgerufen werden:
public class lockMethod {public synchronisierte void busia () {für (int i = 0; i <10000; i ++) {System.out.println (Thread.CurrentThread (). }} public synchronisierte void bussib () {für (int i = 0; i <10000; i ++) {System.out.println (Thread.CurrentThread (). }}}BUSSA ist eine Thread -Klasse, mit der ein Unternehmen verarbeitet und die Bussia () -Methode von LockMethod angerufen wird:
öffentliche Klasse BUSSB erweitert Thread {lockMethod lockMethod; void Deal (lockMethod lockMethod) {this.lockmethod = lockMethod; } @Override public void run () {super.run (); lockMethod.busib (); }}Die TestLockMethod -Klasse verwendet Thread BUSSA und BUSSB für die Geschäftsverarbeitung:
public class testplockMethod erweitert Thread {public static void main (String [] args) {lockMethod lockMethod = new lockMethod (); BUSSA BUSSA = NEW BUSSA (); BUSSB BUSSB = NEW BUSSB (); BUSSA.DEAL (LOCKMETHOD); BUSSB.DEAL (LOCKMETHOD); bussa.start (); Bussb.start (); }}Beim Ausführen des Programms können Sie feststellen, dass BUSSB während der Ausführung von Thread -BUSSA die Funktionsbussib () nicht eingeben kann, da die LockMethod -Objektschloss durch Threadsa erhalten wird.
2. Reduzieren Sie den synchronen Codeblock und sperren Sie nur die Daten
Manchmal synchronisierten einige Leute für die programmierende Bequemlichkeit ein großes Stück Code. Wenn einige Vorgänge in diesem Codeblock nicht mit gemeinsam genutzten Ressourcen zusammenhängen, sollten sie außerhalb des synchronen Blocks platziert werden, um zu vermeiden, dass Schlösser für lange Zeit gehalten werden, wodurch andere Threads in einem Wartezustand bleiben. Insbesondere einige Zyklusoperationen und synchrone E/A -Operationen. Es bedeutet nicht nur, den Synchronisationsblock im Zeilenbereich des Codes zu reduzieren, sondern auch in der Ausführungslogik, der Synchronisationsblock sollte reduziert werden. Fügen Sie beispielsweise mehr bedingte Urteile hinzu und synchronisieren Sie, wenn sie die Bedingungen erfüllen, anstatt nach der Synchronisation bedingte Urteile zu führen, um unnötige Logik zu minimieren, die in den Synchronisationsblock eintritt.
3. Versuchen Sie, keine Schlösser in die Schloss aufzunehmen
Diese Situation tritt oft auf. Nachdem der Thread die A -Sperre erhalten hat, ruft er die Synchronisationsmethode eines anderen Objekts im Synchronisationsmethodenblock auf und erhält die zweite Sperre. Dies kann zu mehreren Sperranfragen in einem Anrufstapel führen. Bei Multi-Threading kann es sehr komplex und schwierig zu analysieren, Ausnahmen zu analysieren, was zu Deadlocks führt. Der folgende Code zeigt dies:
synchronisiert (a) {synchronisiert (b) {}}Oder die Synchronisationsmethode wird im Synchronisationsblock aufgerufen:
synchronisiert (a) {b b = objarrayList.get (0); B.Method (); // Dies ist eine Synchronisierungsmethode}Die Lösung besteht darin, herausspringen und Schlösser hinzufügen und keine Schlösser enthalten:
{B b = null; synchronisiert (a) {b = objarrayList.get (0); } B.Method ();} 4. Privatisieren Sie das Schloss und verwalten Sie das Schloss intern
Es ist sicherer, das Schloss als privates Objekt zu verwenden, und es kann nicht von außen erhalten werden. Ein Objekt kann direkt von anderen Threads gesperrt werden, und der Faden hält beispielsweise die Objektsperrung des Objekts:
Klasse A {public void method1 () {}} Klasse B {public void method1 () {a a = new a (); synchronisiert (a) {// direkt a.method1 () lockern; }}}Auf diese Weise wird die Objektschloss von Objekt A von außen gehalten, sodass es gefährlicher ist, dass die Sperre an mehreren Stellen außen verwendet wird, und es führt auch zu Problemen, den logischen Fluss des Codes zu lesen. Ein besserer Weg ist es, die Schlösser selbst in der Klasse selbst zu verwalten und Synchronisationsvorgänge über Schnittstellen bereitzustellen, wenn das externe Synchronschema erforderlich ist:
Klasse A {private Object lock = new Object (); public void method1 () {synchronized (lock) {}}} Klasse B {public void method1 () {a a = new a (); a.method1 (); }} 5. Führen Sie eine angemessene Verriegelungsabzug durch
Betrachten Sie das folgende Verfahren:
public class GameServer {public map <String, Liste <Player >> tables = new HashMap <String, Liste <Player >> (); public void join (Player Player, Tabelle) {if (Player.GetAccountBalance ()> table.getLimit ()) {synchronisierte (Tabellen) {list <Firne> tablePlayers = tables.get (table.getId ()); if (TablePlayers.size () <9) {TablePlayers.Add (Player); }}}}} public void Leave (Player Player, Tabelle Tabelle) {/*weglassen*/} public void createTable () {/*ausgelassen*/} public void destousTable (Tabelle Tabelle) {/*ausgießen*/}}In diesem Beispiel verwendet die Join -Methode nur eine Synchronisationsschloss, um das List <Seplay> -Objekt in den Tabellen zu erhalten, und dann festzustellen, ob die Anzahl der Spieler weniger als 9 beträgt. Wenn ja, fügen Sie einen Spieler hinzu. Wenn es Tausende von Listen <Player> in Tabellen gibt, ist die Konkurrenz um Tabellenschlösser sehr heftig. Hier können wir in Betracht ziehen, die Sperre zu zerlegen: Nachdem Sie die Daten schnell herausgeholt haben, sperren Sie das List <Seafer> -Objekt, damit andere Threads schnell konkurrieren können, um die Tabellenobjektsperrung zu erhalten:
öffentliche Klassen GameServer {
öffentliche Karte <String,
Liste <Player >> tables = new HashMap <String,
Liste <spieler >> ();
public void Join (Spielerspieler, Tabelle) {
if (Player.GetAccountBalance ()> table.getLimit ()) {
LIST <Stafel> tablePlayers = null;
synchronisiert (Tabellen) {
tablePlayer = tables.get (table.getId ());
}
synchronisiert (Tabellenplayer) {
if (tablePlayers.size () <9) {
TablePlayers.Add (Spieler);
}
}
}
}
public void Urlaub (Spielerspieler, Tabelle) {
/*Weggelassen*/
}
public void createTable () {
/*Weggelassen*/
}
public void destoTable (Tabelle Tabelle) {
/*Weggelassen*/
}
}