Heim  >  Artikel  >  Java  >  Detaillierte Erläuterung der Thread-Sicherheit und Nicht-Thread-Sicherheit in Java

Detaillierte Erläuterung der Thread-Sicherheit und Nicht-Thread-Sicherheit in Java

黄舟
黄舟Original
2017-10-12 10:09:391767Durchsuche

In diesem Artikel wird hauptsächlich die Analyse der Java-Thread-Sicherheit und der Nicht-Thread-Sicherheit vorgestellt, einschließlich der Simulation von Nicht-Thread-Sicherheitsphänomenen und der Implementierung von Thread-Sicherheit. Freunde in Not können sich darauf beziehen und sich austauschen und gemeinsam lernen.

Was ist der Unterschied zwischen ArrayList und Vector? Was ist der Unterschied zwischen HashMap und HashTable? Was ist der Unterschied zwischen StringBuilder und StringBuffer? Dies sind häufige Grundfragen in Java-Interviews. Auf eine solche Frage lautet die Antwort: ArrayList ist nicht Thread-sicher, HashMap ist nicht Thread-sicher, HashTable ist nicht Thread-sicher und StringBuffer ist Thread-sicher. Denn das steht in den „Vollständigen Java-Interviewfragen“, die ich gestern Abend auswendig gelernt habe. Wenn Sie an dieser Stelle weiterhin fragen: Was ist Thread-Sicherheit? Was ist der Unterschied zwischen Thread-sicher und nicht Thread-sicher? Unter welchen Umständen werden sie verwendet? Eine solche Reihe von Fragen sprudelte heraus ...

Nicht-Thread-sichere Phänomensimulation

ArrayList und Vector werden hier verwendet Der Leser wird es tun erklären.

Der folgende Code erstellt eine neue nicht threadsichere ArrayList im Hauptthread und öffnet dann 1000 Threads, um Elemente zu dieser ArrayList hinzuzufügen. Jeder Thread fügt 100 Elemente hinzu und so weiter für alle Threads Die Ausführung ist abgeschlossen. Wie groß sollte diese ArrayList sein? Sollten 100.000 sein?


public class Main 
{ 
  public static void main(String[] args) 
  { 
    // 进行10次测试 
    for(int i = 0; i < 10; i++) 
    { 
      test(); 
    } 
  } 
  public static void test() 
  { 
    // 用来测试的List 
    List<Object> list = new ArrayList<Object>(); 
    // 线程数量(1000) 
    int threadCount = 1000; 
    // 用来让主线程等待threadCount个子线程执行完毕 
    CountDownLatch countDownLatch = new CountDownLatch(threadCount); 
    // 启动threadCount个子线程 
    for(int i = 0; i < threadCount; i++) 
    { 
      Thread thread = new Thread(new MyThread(list, countDownLatch)); 
      thread.start(); 
    } 
    try 
    { 
      // 主线程等待所有子线程执行完成,再向下执行 
      countDownLatch.await(); 
    } 
    catch (InterruptedException e) 
    { 
      e.printStackTrace(); 
    } 
    // List的size 
    System.out.println(list.size()); 
  } 
} 
class MyThread implements Runnable 
{ 
  private List<Object> list; 
  private CountDownLatch countDownLatch; 
  public MyThread(List<Object> list, CountDownLatch countDownLatch) 
  { 
    this.list = list; 
    this.countDownLatch = countDownLatch; 
  } 
  public void run() 
  { 
    // 每个线程向List中添加100个元素 
    for(int i = 0; i < 100; i++) 
    { 
      list.add(new Object()); 
    } 
    // 完成一个子线程 
    countDownLatch.countDown(); 
  } 
}

Das Obige wurde 10 Mal getestet (warum 10 Mal testen? Weil die Nicht-Thread-Sicherheit nicht jedes Mal Probleme verursacht).

Ausgabeergebnisse:


99946
100000
100000
100000
99998
99959
100000
99975
100000
99996

Die obigen Ausgabeergebnisse zeigen, dass nicht jedes Testergebnis 100000 ist, es gibt Nach mehreren Bei Tests betrug die Größe der ArrayList weniger als 100.000 und von Zeit zu Zeit wurde sogar eine IndexOutOfBoundsException ausgelöst. (Wenn dieses Phänomen nicht auftritt, können Sie es noch ein paar Mal versuchen)

Dies ist ein Problem, das durch Nicht-Thread-Sicherheit verursacht wird. Wenn der obige Code in einer Produktionsumgebung verwendet wird, lauern versteckte Gefahren und Fehler.

Verwenden Sie dann den threadsicheren Vektor, um den obigen Code an einer Stelle zu testen, in der test()-Methode,


List<Object> list = new ArrayList<Object>();

Wechseln Sie zu


List<Object> list = new Vector<Object>();

und führen Sie das Programm erneut aus.

Ausgabeergebnis:


100000
100000
100000
100000
100000
100000
100000
100000
100000
100000

Nachdem ich es noch ein paar Mal ausgeführt hatte, stellte ich fest, dass sie alle 100.000 waren , ohne Probleme. Da Vector Thread-sicher ist, treten keine Probleme auf, wenn mehrere Threads dasselbe Vector-Objekt bearbeiten.

Versuchen Sie, zu LinkedList zu wechseln. Ähnliche Probleme treten auch bei ArrayList auf, da LinkedList ebenfalls nicht threadsicher ist.

So wählen Sie zwischen den beiden

Nicht-Thread-Sicherheit bedeutet, dass Probleme auftreten können, wenn mehrere Threads dasselbe Objekt bedienen. Thread-Sicherheit bedeutet, dass es kein Problem gibt, wenn mehrere Threads dasselbe Objekt bearbeiten.

Thread-Sicherheit muss viele synchronisierte Schlüsselwörter zur Synchronisationssteuerung verwenden, was zwangsläufig zu einer Leistungseinbuße führt.

Wenn also mehrere Threads dasselbe Objekt bedienen, verwenden Sie den threadsicheren Vektor, andernfalls verwenden Sie die effizientere ArrayList.

Non-thread-safe!=Unsafe

Jemand hat während der Verwendung eine falsche Ansicht: I Das Programm ist Multithreaded und kann ArrayList nicht verwenden. Die Verwendung von Vector ist sicher.

Nicht-Thread-Sicherheit bedeutet nicht, dass es nicht in einer Multithread-Umgebung verwendet werden kann. Beachten Sie, was ich oben gesagt habe: Mehrere Threads arbeiten mit demselben Objekt. Beachten Sie, dass es sich um dasselbe Objekt handelt. Die Top-Simulation ist beispielsweise eine neue ArrayList im Hauptthread und dann betreiben mehrere Threads dasselbe ArrayList-Objekt.

Wenn es in jedem Thread eine neue ArrayList gibt und diese ArrayList nur in diesem Thread verwendet wird, dann gibt es definitiv kein Problem.

Implementierung der Thread-Sicherheit

Thread-Sicherheit wird durch Thread-Synchronisationssteuerung erreicht, bei der es sich um das synchronisierte Schlüsselwort handelt.

Hier habe ich Code verwendet, um einen nicht-thread-sicheren Zähler und einen thread-sicheren Zähler Counter zu implementieren, und Multi-Thread-Tests mit ihnen durchgeführt.

Nicht-Thread-sicherer Zähler:


public class Main 
{ 
  public static void main(String[] args) 
  { 
    // 进行10次测试 
    for(int i = 0; i < 10; i++) 
    { 
      test(); 
    } 
  } 
  public static void test() 
  { 
    // 计数器 
    Counter counter = new Counter(); 
    // 线程数量(1000) 
    int threadCount = 1000; 
    // 用来让主线程等待threadCount个子线程执行完毕 
    CountDownLatch countDownLatch = new CountDownLatch(threadCount); 
    // 启动threadCount个子线程 
    for(int i = 0; i < threadCount; i++) 
    { 
      Thread thread = new Thread(new MyThread(counter, countDownLatch)); 
      thread.start(); 
    } 
    try 
    { 
      // 主线程等待所有子线程执行完成,再向下执行 
      countDownLatch.await(); 
    } 
    catch (InterruptedException e) 
    { 
      e.printStackTrace(); 
    } 
    // 计数器的值 
    System.out.println(counter.getCount()); 
  } 
} 
class MyThread implements Runnable 
{ 
  private Counter counter; 
  private CountDownLatch countDownLatch; 
  public MyThread(Counter counter, CountDownLatch countDownLatch) 
  { 
    this.counter = counter; 
    this.countDownLatch = countDownLatch; 
  } 
  public void run() 
  { 
    // 每个线程向Counter中进行10000次累加 
    for(int i = 0; i < 10000; i++) 
    { 
      counter.addCount(); 
    } 
    // 完成一个子线程 
    countDownLatch.countDown(); 
  } 
} 
class Counter 
{ 
  private int count = 0; 
  public int getCount() 
  { 
    return count; 
  } 
  public void addCount() 
  { 
    count++; 
  } 
}

Im obigen Testcode werden 1000 Threads geöffnet, jeder Thread Wenn Sie den Zähler 10.000 Mal akkumulieren, sollte das endgültige Ausgabeergebnis 10.000.000 sein.

Der Zähler im obigen Code wird jedoch nicht synchron gesteuert und ist daher nicht threadsicher.

Ausgabeergebnis:


9963727
9973178
9999577
9987650
9988734
9988665
9987820
9990847
9992305
9972233

Ändern Sie den Zähler leicht in einen Thread-sicheren Zähler:


class Counter 
{ 
  private int count = 0; 
  public int getCount() 
  { 
    return count; 
  } 
  public synchronized void addCount() 
  { 
    count++; 
  } 
}

Das Obige fügt lediglich eine synchronisierte Synchronisationssteuerung zur addCount()-Methode hinzu und wird zu einem threadsicheren Zähler. Führen Sie das Programm erneut aus.

Ausgabeergebnis:


10000000
10000000
10000000
10000000
10000000
10000000
10000000
10000000
10000000
10000000

Zusammenfassung

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Thread-Sicherheit und Nicht-Thread-Sicherheit in Java. 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