Heim  >  Artikel  >  Java  >  Einführung in verschiedene Methoden der Java-Multithread-Synchronisierung

Einführung in verschiedene Methoden der Java-Multithread-Synchronisierung

黄舟
黄舟Original
2017-09-20 10:04:061493Durchsuche

In diesem Artikel werden hauptsächlich relevante Informationen zu verschiedenen Methoden zur Synchronisierung von Java-Multithreads vorgestellt. Hier sind 5 Methoden, auf die sich Freunde in Not beziehen können:

Mehrere Methoden zur Synchronisierung von Java-Multithreads

1. Einleitung

Vor ein paar Tagen wurde ich vom Meister gefoltert wieder Grundkenntnisse. Kommen wir ohne weitere Umschweife zum Punkt.

2. Warum Thread-Synchronisierung erforderlich ist

Denn wenn wir mehrere Threads haben, die gleichzeitig auf eine Variable oder ein Objekt zugreifen, wenn es in diesen Leser gibt Threads Wenn ein weiterer Schreibvorgang ausgeführt wird, werden der Variablenwert oder der Status des Objekts verwirrt, was zu einer Programmausnahme führt. Wenn beispielsweise ein Bankkonto von zwei Threads gleichzeitig betrieben wird, hebt einer 100 Yuan ab und der andere zahlt 100 Yuan ein. Angenommen, das Konto hat ursprünglich 0 Blöcke, wenn der Auszahlungsthread und der Einzahlungsthread gleichzeitig stattfinden. Die Geldabhebung ist fehlgeschlagen und der Kontostand beträgt 100. Die Geldabhebung ist erfolgreich und der Kontostand beträgt 0. Welches ist es also? Es ist schwer zu sagen. Daher soll die Multithread-Synchronisation dieses Problem lösen.

3. Code bei Nichtsynchronisierung

Bank.java


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count =0;//账户余额 

  //存钱 
  public void addMoney(int money){ 
    count +=money; 
    System.out.println(System.currentTimeMillis()+"存进:"+money); 
  } 

  //取钱 
  public void subMoney(int money){ 
    if(count-money < 0){ 
      System.out.println("余额不足"); 
      return; 
    } 
    count -=money; 
    System.out.println(+System.currentTimeMillis()+"取出:"+money); 
  } 

  //查询 
  public void lookMoney(){ 
    System.out.println("账户余额:"+count); 
  } 
}

SyncThreadTest .java


package threadTest; 
/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public class SyncThreadTest { 

  public static void main(String args[]){ 
    final Bank bank=new Bank(); 

    Thread tadd=new Thread(new Runnable() { 

      @Override 
      public void run() { 
        // TODO Auto-generated method stub 
        while(true){ 
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
          } 
          bank.addMoney(100); 
          bank.lookMoney(); 
          System.out.println("\n"); 

        } 
      } 
    }); 

    Thread tsub = new Thread(new Runnable() { 

      @Override 
      public void run() { 
        // TODO Auto-generated method stub 
        while(true){ 
          bank.subMoney(100); 
          bank.lookMoney(); 
          System.out.println("\n"); 
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
          }   
        } 
      } 
    }); 
    tsub.start(); 

    tadd.start(); 
  } 

}

Der Code ist sehr einfach, daher werde ich ihn nicht erklären. Mal sehen, wie die Ergebnisse aussehen. Ich habe einen Teil davon herausgeschnitten. Es ist sehr chaotisch und ich kann es nicht verstehen.


余额不足 
账户余额:0 

余额不足 
账户余额:100 

1441790503354存进:100 
账户余额:100 

1441790504354存进:100 
账户余额:100 

1441790504354取出:100 
账户余额:100 

1441790505355存进:100 
账户余额:100 

1441790505355取出:100 
账户余额:100

4. Code bei Verwendung der Synchronisierung

(1) Synchronisierungsmethode:

Es gibt eine Methode zum Ändern des synchronisierten Schlüsselworts. Da jedes Objekt in Java über eine integrierte Sperre verfügt, schützt die integrierte Sperre die gesamte Methode, wenn eine Methode mit diesem Schlüsselwort geändert wird. Bevor Sie diese Methode aufrufen, müssen Sie die integrierte Sperre erhalten, andernfalls wird sie blockiert.
Modified Bank.java


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count =0;//账户余额 

  //存钱 
  public synchronized void addMoney(int money){ 
    count +=money; 
    System.out.println(System.currentTimeMillis()+"存进:"+money); 
  } 

  //取钱 
  public synchronized void subMoney(int money){ 
    if(count-money < 0){ 
      System.out.println("余额不足"); 
      return; 
    } 
    count -=money; 
    System.out.println(+System.currentTimeMillis()+"取出:"+money); 
  } 

  //查询 
  public void lookMoney(){ 
    System.out.println("账户余额:"+count); 
  } 
}

Sehen Sie sich die laufenden Ergebnisse an:


余额不足 
账户余额:0 

余额不足 
账户余额:0 

1441790837380存进:100 
账户余额:100 

1441790838380取出:100 
账户余额:0 
1441790838380存进:100 
账户余额:100 

1441790839381取出:100 
账户余额:0

I Ich habe das Gefühl, dass ich es sofort verstehen kann.

Hinweis: Das synchronisierte Schlüsselwort kann auch statische Methoden ändern. Wenn die statische Methode zu diesem Zeitpunkt aufgerufen wird, wird die gesamte Klasse gesperrt

(2) Synchronisierter Codeblock

Das heißt, der durch das synchronisierte Schlüsselwort geänderte Anweisungsblock. Der durch dieses Schlüsselwort geänderte Kontoauszugsblock wird automatisch mit einer integrierten Sperre hinzugefügt, um eine Synchronisierung zu erreichen.
Der Bank.java-Code lautet wie folgt:


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count =0;//账户余额 

  //存钱 
  public  void addMoney(int money){ 

    synchronized (this) { 
      count +=money; 
    } 
    System.out.println(System.currentTimeMillis()+"存进:"+money); 
  } 

  //取钱 
  public  void subMoney(int money){ 

    synchronized (this) { 
      if(count-money < 0){ 
        System.out.println("余额不足"); 
        return; 
      } 
      count -=money; 
    } 
    System.out.println(+System.currentTimeMillis()+"取出:"+money); 
  } 

  //查询 
  public void lookMoney(){ 
    System.out.println("账户余额:"+count); 
  } 
}

Die Laufergebnisse sind wie folgt:


余额不足 
账户余额:0 

1441791806699存进:100 
账户余额:100 

1441791806700取出:100 
账户余额:0 

1441791807699存进:100 
账户余额:100

Der Effekt ist ähnlich wie bei Methode eins.

Hinweis: Die Synchronisierung ist ein Vorgang mit hohem Overhead, daher sollte der synchronisierte Inhalt minimiert werden. Normalerweise ist es nicht erforderlich, die gesamte Methode zu synchronisieren. Verwenden Sie einfach synchronisierte Codeblöcke, um den Schlüsselcode zu synchronisieren.

(3) Verwenden Sie spezielle Domänenvariablen (Volatile), um eine Thread-Synchronisierung zu erreichen.

a Das Schlüsselwort volatile bietet einen sperrfreien Mechanismus für den Zugriff auf Domänenvariablen. b ist äquivalent zu Teilen Sie der virtuellen Maschine mit, dass dieses Feld von anderen Threads aktualisiert werden kann. Daher muss das Feld jedes Mal neu berechnet werden, anstatt den Wert im Register zu verwenden. Volatile bietet keine atomaren Operationen und kann dies auch nicht Es wird verwendet, um endgültige Typen zu ändern. Der

Bank.java-Code lautet wie folgt:


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private volatile int count = 0;// 账户余额 

  // 存钱 
  public void addMoney(int money) { 

    count += money; 
    System.out.println(System.currentTimeMillis() + "存进:" + money); 
  } 

  // 取钱 
  public void subMoney(int money) { 

    if (count - money < 0) { 
      System.out.println("余额不足"); 
      return; 
    } 
    count -= money; 
    System.out.println(+System.currentTimeMillis() + "取出:" + money); 
  } 

  // 查询 
  public void lookMoney() { 
    System.out.println("账户余额:" + count); 
  } 
}

Was ist der laufende Effekt? ?


余额不足 
账户余额:0 

余额不足 
账户余额:100 

1441792010959存进:100 
账户余额:100 

1441792011960取出:100 
账户余额:0 

1441792011961存进:100 
账户余额:100

Sind Sie schon wieder verwirrt? Warum ist das so? Dies liegt daran, dass Volatile keine atomaren Operationen garantieren kann und daher synchronisierte Operationen nicht durch Volatilität ersetzen kann. Darüber hinaus verhindert Volatilität, dass der Compiler den Code optimiert. Wenn Sie ihn also nicht verwenden können, wenden Sie ihn nicht an. Sein Prinzip besteht darin, dass ein Thread jedes Mal, wenn er auf eine flüchtig geänderte Variable zugreifen möchte, diese aus dem Speicher liest, anstatt sie aus dem Cache zu lesen, sodass der Variablenwert, auf den jeder Thread zugreift, derselbe ist. Dadurch wird die Synchronisation gewährleistet.

(4) Verwenden Sie Wiedereintrittssperren, um eine Thread-Synchronisierung zu erreichen

Ein neues java.util.concurrent-Paket wurde in JavaSE5.0 hinzugefügt, um die Synchronisierung zu unterstützen. Die ReentrantLock-Klasse ist eine wiedereintrittsfähige, sich gegenseitig ausschließende Sperre, die die Lock-Schnittstelle implementiert. Sie weist das gleiche grundlegende Verhalten und die gleiche Semantik wie die Verwendung synchronisierter Methoden und Blöcke auf und erweitert ihre Funktionen. Häufig verwendete Methoden der ReentrantLock-Klasse sind: ReentrantLock(): Erstellen einer ReentrantLock-Instanz lock(): Abrufen der Sperre unlock(): Freigeben der Sperre Hinweis: ReentrantLock() verfügt auch über einen Konstruktor, der eine faire Sperre erstellen kann, aber weil er kann das Programm erheblich reduzieren. Aus betrieblichen Gründen wird nicht empfohlen, die Bank.java-Codeänderung wie folgt zu verwenden:


package threadTest; 

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

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count = 0;// 账户余额 

  //需要声明这个锁 
  private Lock lock = new ReentrantLock(); 

  // 存钱 
  public void addMoney(int money) { 
    lock.lock();//上锁 
    try{ 
    count += money; 
    System.out.println(System.currentTimeMillis() + "存进:" + money); 

    }finally{ 
      lock.unlock();//解锁 
    } 
  } 

  // 取钱 
  public void subMoney(int money) { 
    lock.lock(); 
    try{ 

    if (count - money < 0) { 
      System.out.println("余额不足"); 
      return; 
    } 
    count -= money; 
    System.out.println(+System.currentTimeMillis() + "取出:" + money); 
    }finally{ 
      lock.unlock(); 
    } 
  } 

  // 查询 
  public void lookMoney() { 
    System.out.println("账户余额:" + count); 
  } 
}

Was ist der laufende Effekt?


余额不足 
账户余额:0 

余额不足 
账户余额:0 

1441792891934存进:100 
账户余额:100 

1441792892935存进:100 
账户余额:200 

1441792892954取出:100 
账户余额:100

Der Effekt ist ähnlich wie bei den ersten beiden Methoden.

Wenn das synchronisierte Schlüsselwort die Anforderungen der Benutzer erfüllen kann, verwenden Sie synchronisiert, da es den Code vereinfachen kann. Wenn Sie erweiterte Funktionen benötigen, verwenden Sie die ReentrantLock-Klasse. Achten Sie zu diesem Zeitpunkt darauf, die Sperre rechtzeitig aufzuheben, da sonst ein Deadlock auftritt. Die Sperre wird normalerweise im endgültigen Code freigegeben

(5) Verwendung Lokale Variablen zum Erreichen der Thread-Synchronisation

Bank.java-Code lautet wie folgt:


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){ 

    @Override 
    protected Integer initialValue() { 
      // TODO Auto-generated method stub 
      return 0; 
    } 

  }; 

  // 存钱 
  public void addMoney(int money) { 
    count.set(count.get()+money); 
    System.out.println(System.currentTimeMillis() + "存进:" + money); 

  } 

  // 取钱 
  public void subMoney(int money) { 
    if (count.get() - money < 0) { 
      System.out.println("余额不足"); 
      return; 
    } 
    count.set(count.get()- money); 
    System.out.println(+System.currentTimeMillis() + "取出:" + money); 
  } 

  // 查询 
  public void lookMoney() { 
    System.out.println("账户余额:" + count.get()); 
  } 
}

Ausführungseffekt:


余额不足 
账户余额:0 

余额不足 
账户余额:0 

1441794247939存进:100 
账户余额:100 

余额不足 
1441794248940存进:100 
账户余额:0 

账户余额:200 

余额不足 
账户余额:0 

1441794249941存进:100 
账户余额:300

Sehen Sie, ich war zunächst verwirrt über den Operationseffekt. Warum ist nur das Speichern, aber nicht das Abheben erlaubt? Schauen Sie sich das Prinzip von ThreadLocal an:

Wenn Sie ThreadLocal zum Verwalten von Variablen verwenden, erhält jeder Thread, der die Variable verwendet, eine Kopie der Variablen. Die Kopien sind unabhängig voneinander, sodass jeder Thread seine eigene Kopie der Variablen nach Belieben ändern kann, ohne dass es zu Problemen kommt Einfluss auf andere Threads. Jetzt verstehen Sie, dass jeder Thread eine Kopie ausführt, was bedeutet, dass das Einzahlen und Abheben von Geld zwei Konten mit demselben Wissensnamen sind. Der obige Effekt wird also auftreten.

ThreadLocal- und Synchronisationsmechanismus

a. ThreadLocal und der Synchronisationsmechanismus dienen beide dazu, das Problem von Zugriffskonflikten derselben Variablen in Multithreads zu lösen. b „Raum gegen Zeit“-Methode, letztere übernimmt die Methode des „Austauschs von Zeit gegen Raum“

Das obige ist der detaillierte Inhalt vonEinführung in verschiedene Methoden der Java-Multithread-Synchronisierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn