Heim >Java >javaLernprogramm >Detaillierte Erläuterung der Thread-Sicherheit und Nicht-Thread-Sicherheit in Java
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!